browser-use/browser-use · error · ModelProviderError
Failed to parse structured output from model response
Error message
Failed to parse structured output from model response
What it means
OpenRouter chat wrapper's structured-output path: after a successful completion, if `response.choices[0].message.content is None` it raises ModelProviderError (500) because there is no JSON text to feed `output_format.model_validate_json`. OpenRouter routes to many upstream providers, and some return null content (reasoning-only, tool-call-only, or upstream errors mapped to empty content).
Source
Thrown at browser_use/llm/openrouter/chat.py:189
}
# Return structured response
response = await self.get_client().chat.completions.create(
model=self.model,
messages=openrouter_messages,
temperature=self.temperature,
top_p=self.top_p,
seed=self.seed,
response_format=ResponseFormatJSONSchema(
json_schema=response_format_schema,
type='json_schema',
),
extra_headers=extra_headers,
**(self.extra_body or {}),
)
if response.choices[0].message.content is None:
raise ModelProviderError(
message='Failed to parse structured output from model response',
status_code=500,
model=self.name,
)
usage = self._get_usage(response)
parsed = output_format.model_validate_json(response.choices[0].message.content)
return ChatInvokeCompletion(
completion=parsed,
usage=usage,
)
except RateLimitError as e:
raise ModelRateLimitError(message=e.message, model=self.name) from e
except APIConnectionError as e:
raise ModelProviderError(message=str(e), model=self.name) from eView on GitHub (pinned to 6c73fced2f)
Solutions
- Switch the OpenRouter model slug to one with native structured-output/JSON support (e.g. major frontier models) and re-run
- Retry once — transient null-content responses from upstream providers are common on OpenRouter
- Check the OpenRouter dashboard/logs for the upstream finish_reason (filter, length, tool_calls) for that request
- Avoid `:free` variants for structured output; use the paid tier of the same model
Example fix
# before llm = ChatOpenRouter(model='some-model:free') # after llm = ChatOpenRouter(model='openai/gpt-4.1-mini')
Defensive patterns
Strategy: retry
Try / catch
try:
out = await llm.invoke(msgs, output_format=Schema)
except ModelProviderError as e:
if 'parse structured output' in e.message:
llm.model = FALLBACK_MODEL # e.g. a frontier model with native JSON mode
out = await llm.invoke(msgs, output_format=Schema)
else:
raise Prevention
- Avoid ':free' OpenRouter models for structured output
- Keep a known-good fallback model slug for schema-driven calls
When it happens
Trigger: Calling ChatOpenRouter with an `output_format` where the routed model returns null content — common with free-tier or non-JSON-native models, models that answer via tool_calls, or upstream providers whose JSON mode is unimplemented. Also occurs when OpenRouter routes to a model that spent its output on hidden reasoning.
Common situations: Using `:free` OpenRouter models that don't support structured output; picking a model variant without JSON-mode support; hitting an upstream content filter; upstream provider flakiness returning empty completions.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Failed to parse structured output from model response
- Failed to parse structured output from model response - empt
- Model output was truncated at max_tokens={self.max_tokens};
- Expected tool use in response but none found
- Expected tool use in response but none found
AI-assisted analysis of browser-use/browser-use@6c73fced2f (2026-08-14).
Data as JSON: /api/errors/cd545e1835204823.
Report an issue: GitHub.