#302645·homebrew-core

ollama: MLX structured output broken — libollama_xgrammar.dylib is never built (xgrammar is FetchContent'd at build time, no resource block)

Author: RobitonCreated Sep 6, 2026Updated Sep 15, 2026

brew config AND brew doctor output OR brew gist-logs <formula> link

brew doctor
Your system is ready to brew.
brew config
HOMEBREW_VERSION: 6.0.21
ORIGIN: https://github.com/Homebrew/brew
HEAD: 560147012b9678b42ef5e83b690f0895552d1366
Branch: stable
HOMEBREW_PREFIX: /opt/homebrew
CPU: 18-core 64-bit arm_sotra
Clang: 21.0.0 build 2100
macOS: 26.6.2-arm64
CLT: 26.6.0.0.1781586589
Rosetta 2: false

Verification

  • My brew doctor output says Your system is ready to brew. and am still able to reproduce my issue.
  • I ran brew update and am still able to reproduce my issue.
  • I have resolved all warnings from brew doctor and that did not fix my problem.
  • I searched for recent similar issues and found no duplicates.
  • My issue is not about a failure to build a formula from source.
  • I did not use AI/LLM to create this issue, or I disclosed the tool and model used; I will answer maintainer questions myself without AI/LLM.

Disclosure: the investigation and the first draft of this report were produced with Claude (Anthropic). Every claim below was verified by running the commands shown. I will answer maintainer questions myself.

What were you trying to do (and why)?

Use Ollama's structured-output feature (format with a JSON schema) against an MLX-tagged model on Apple Silicon, so that a local model returns schema-conforming JSON instead of prose.

What happened (include all command output)?

The request fails with HTTP 501 on any MLX-tagged model, while the identical request succeeds on a GGUF model:

$ curl -s localhost:11434/api/generate -d '{
    "model": "qwen3.6:27b-mlx",
    "prompt": "Bench press 84 kg for 5, 5, 4.",
    "stream": false,
    "format": {"type":"object","properties":{"movement":{"type":"string"}},"required":["movement"]}
  }'
{"error":"structured output is unavailable"}

The server log gives the cause:

level=WARN source=grammar.go:75 msg="Structured output is unavailable"
  error="xgrammar library not found at
  /opt/homebrew/Cellar/ollama/0.33.2/libexec/lib/ollama/mlx_metal_v3/libollama_xgrammar.dylib:
  no such file or directory"

The file is absent:

$ find /opt/homebrew -name 'libollama_xgrammar*'
$ ls /opt/homebrew/Cellar/ollama/0.33.2/libexec/lib/ollama/mlx_metal_v3/
libmlxc.dylib   libmlx.dylib   mlx.metallib

Cause. Upstream builds xgrammar by fetching it over the network at build time — cmake/mlx/CMakeLists.txt:

cmake
FetchContent_Declare(xgrammar
    GIT_REPOSITORY "https://github.com/mlc-ai/xgrammar.git"
    GIT_TAG v0.2.5)

Homebrew builds in a network-isolated sandbox, and the formula declares no resource for xgrammar the way it does for llama.cpp (Formula/o/ollama.rb). So the target is skipped and the dylib is never produced. grep -i xgrammar Formula/o/ollama.rb returns nothing at 0.33.2 or at current HEAD (0.33.3).

The official upstream build does ship it. Listing ollama-darwin.tgz for v0.33.3:

XGRAMMAR_LICENSE
XGRAMMAR_NOTICE
mlx_metal_v3/libmlxc.dylib
mlx_metal_v3/libollama_xgrammar.dylib

Same relative path the WARN reports missing.

What did you expect to happen?

That format with a JSON schema would be honoured on MLX models, as it is on GGUF models and as it is with the official upstream darwin build.

Step-by-step reproduction instructions (by running brew commands)

bash
brew update
brew install ollama
brew services start ollama          # or: ollama serve
ollama pull qwen3.6:27b-mlx         # any MLX-tagged model
curl -s localhost:11434/api/generate -d '{
  "model": "qwen3.6:27b-mlx",
  "prompt": "Bench press 84 kg for 5, 5, 4.",
  "stream": false,
  "format": {"type":"object","properties":{"movement":{"type":"string"}},"required":["movement"]}
}'
# -> {"error":"structured output is unavailable"}

find "$(brew --prefix)" -name 'libollama_xgrammar*'
# -> no output

Reproduced on two Apple Silicon machines (M3 Max and M5 Max), both Homebrew ollama 0.33.2.