we-promise/sure · error · Provider::Openai::Error
Could not parse JSON from PDF processing response: #{raw.tru
Error message
Could not parse JSON from PDF processing response: #{raw.truncate(200)} What it means
Raised by Provider::Openai::PdfProcessor#parse_json_flexibly when the model's PDF-analysis reply defeats every recovery strategy: direct JSON.parse, fence stripping, fragment unwrapping, and a final regex grab of the outermost {...}. Crucially, the vision path (process_with_vision) cannot use response_format json_object — the code comments note response_format is incompatible with vision — so it only prompts for JSON; models that answer with markdown fences plus prose, or get truncated by max_tokens, land here. The message embeds the first 200 chars of raw output for diagnosis.
Source
Thrown at app/models/provider/openai/pdf_processor.rb:264
# Try to extract JSON from markdown code blocks
if raw =~ /```(?:json)?\s*(\{[\s\S]*?\})\s*```/m
begin
return JSON.parse($1)
rescue JSON::ParserError
# Continue to next strategy
end
end
# Try to find any JSON object
if raw =~ /(\{[\s\S]*\})/m
begin
return JSON.parse($1)
rescue JSON::ParserError
# Fall through to error
end
end
raise Provider::Openai::Error, "Could not parse JSON from PDF processing response: #{raw.truncate(200)}"
end
end
View on GitHub (pinned to e69894adb9)
Solutions
- Inspect the truncated raw in the message: unclosed JSON means raise max_tokens or process fewer pages per call.
- Strengthen the prompt suffix already present ('IMPORTANT: Respond with valid JSON only') — also demand 'no markdown fences, first character must be {'.
- Retry once: ask a follow-up 'Return only the JSON object' with the previous output as context, or re-run the same chunk (nondeterministic fence usage often clears).
- Improve the last-resort extraction: try the first balanced brace sequence or JSON.parse with truncation repair before giving up.
Example fix
# before response = client.chat(parameters: params) parse_response_generic(response) # after params[:max_tokens] = [ params[:max_tokens].to_i, 4096 ].max # avoid truncated JSON response = client.chat(parameters: params) parse_response_generic(response)
Defensive patterns
Strategy: retry
Try / catch
begin
result = processor.process
rescue Provider::Openai::Error => e
raise unless e.message.include?("Could not parse JSON")
result = processor.process # one retry: fence-wrapped vision answers are nondeterministic
end Prevention
- Budget max_tokens for the full multi-page vision answer; truncation is the top cause of unparseable JSON.
- Prompt 'first character must be { — no markdown fences' since response_format is unusable on the vision path.
- Cap pages per request (the code already limits to 5) and split larger documents into multiple calls.
When it happens
Trigger: Vision model wrapping its answer in ```json fences with commentary that defeats the regex (e.g. multiple fenced blocks, or braces inside prose); truncation at max_tokens leaving unclosed JSON; model answering in plain prose ignoring the JSON-only instruction; extracted-JSON regex matching an outer {...} that is itself invalid (nested unbalanced braces).
Common situations: First 5 pages of dense statements consuming the budget; gpt-4o/gpt-4.1 vision replies with preamble text; custom vision-capable models with weaker instruction following; max_response_tokens configured low to cut cost.
Related errors
- Could not parse JSON from response: #{raw.truncate(200)}
- Invalid JSON in native categorization: #{e.message}
- Model does not support PDF/vision processing: #{effective_mo
- Could not find categorizations in response
- Could not extract text from PDF
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/65b725dcbca8db4d.
Report an issue: GitHub.