How no-mistakes Structured Output Parsers Extract JSON from Agent Responses
no-mistakes extracts JSON from AI agent responses by unmarshaling raw bytes from AgentResult.Output into concrete Go structs, with automatic fallback to plain-text processing when parsing fails.
The no-mistakes CLI orchestrates AI agents like Claude, Codex, and Open-Code to automate software engineering tasks. Each agent returns a mixed response containing human-readable text alongside an optional JSON block defined by a schema. Understanding how the structured output parser handles this extraction is critical for building reliable agentic pipelines that can distinguish between conversational output and actionable structured data.
The Agent Result Structure
Every agent invocation returns an AgentResult struct defined in internal/agent/agent.go. This struct encapsulates the raw response bytes in its Output field, which may contain a JSON block representing the step's structured findings.
The pipeline steps interact with this raw data through a consistent interface:
type AgentResult struct {
Output []byte // Raw bytes potentially containing JSON
// Additional fields for metadata, errors, etc.
}
When a step receives this result, it treats Output as the canonical source for structured data extraction. The bytes are not pre-processed or validated by the agent layer; instead, the parsing responsibility falls to the individual pipeline step consuming the result.
Detecting and Parsing Structured Output
The extraction pipeline follows a strict two-phase validation process implemented across step implementations in internal/pipeline/steps/.
Phase 1: Empty Output Detection
Steps first verify that the agent supplied any structured content. In internal/pipeline/steps/document.go (lines 116-120), the code checks for zero-length output:
if len(result.Output) == 0 {
sctx.Log("missing structured output, requiring approval")
// Trigger human review path
return
}
When result.Output is empty, the step immediately diverts to a human-in-the-loop approval workflow rather than attempting to parse nil data.
Phase 2: JSON Unmarshaling
For non-empty outputs, steps attempt to decode the JSON into a concrete Go struct. This pattern appears in internal/pipeline/steps/lint.go (line 71) and internal/pipeline/steps/review.go (line 196):
var findings internaltypes.Findings
if err := json.Unmarshal(result.Output, &findings); err != nil {
sctx.Log("could not parse structured output, using text response")
// Fallback to plain-text handling
return
}
// Use findings for auto-fix generation and reporting
The findings variable corresponds to the schema-defined structure in internal/types/findings.go. The parser relies on Go's standard json.Unmarshal behavior to map agent-generated JSON into type-safe Go structs.
Error Handling and Fallback Strategy
When json.Unmarshal fails, the pipeline implements a graceful degradation strategy. Rather than failing the entire step, the system falls back to treating the agent response as plain text.
This fallback mechanism serves dual purposes:
- For lint and test steps: The raw text is treated as a textual finding, allowing the pipeline to continue with reduced automation
- For document steps: The failure triggers mandatory human review, ensuring critical documentation changes receive manual verification
The e2e test suite in internal/e2e/journey_test.go (lines 1583-1596) explicitly validates this behavior, ensuring that even malformed JSON gets recorded in the findings field for later replay and debugging.
Schema Validation and Type Safety
While agents may be configured with a JSON schema via AgentConfig.JSONSchema, the runtime parser does not enforce schema constraints dynamically. Instead, the pipeline relies on static type safety through Go struct definitions.
The schema serves as documentation for the agent's prompt, but the actual extraction depends on whether the JSON can be unmarshaled into the target struct. This approach prioritizes performance and simplicity over runtime schema validation, assuming that well-typed Go structs provide sufficient contract enforcement.
Agent-Specific Structured Output Handling
Different AI backends implement distinct strategies for signaling missing structured output.
Claude returns a specific error type defined in internal/agent/claude.go (line 22):
var errNoStructuredOutput = errors.New("agent produced no structured output")
Open-Code and Rovo-Dev track missing output metrics through OnChunk callbacks in internal/agent/opencode.go (line 165), allowing the system to record telemetry when agents fail to generate JSON blocks during streaming responses.
These agent-specific implementations ensure consistent behavior across different LLM providers while maintaining a unified interface for the pipeline steps.
Summary
- Raw extraction: Pipeline steps access
AgentResult.Outputfrominternal/agent/agent.goto obtain raw JSON bytes - Unmarshaling: Use
json.Unmarshal(result.Output, &findings)to convert bytes into concrete Go structs defined ininternal/types/findings.go - Missing data handling: Check
len(result.Output) == 0before parsing to trigger human approval workflows - Fallback strategy: When JSON parsing fails, log the error and fall back to plain-text processing or human review
- Agent variations: Claude signals missing output via
errNoStructuredOutput, while Open-Code tracks metrics through streaming callbacks
Frequently Asked Questions
What happens when an agent returns no JSON block?
The step checks if result.Output is empty and logs "missing structured output, requiring approval". For document steps, this forces human review. For lint or test steps, the pipeline may continue with alternative processing strategies.
How does no-mistakes handle invalid JSON syntax?
When json.Unmarshal returns an error, the step logs "could not parse structured output, using text response" and falls back to plain-text handling. The raw output is still preserved in the findings structure to ensure data is not lost during e2e test replays.
Is JSON schema enforced at runtime?
No. While AgentConfig.JSONSchema defines the expected structure for the agent's prompt, the parser only validates that the JSON can be unmarshaled into the target Go struct. The schema itself is not enforced dynamically during extraction.
Which agents have special structured output handling?
Claude uses errNoStructuredOutput defined in internal/agent/claude.go to signal missing JSON blocks. Open-Code and Rovo-Dev implement specialized metrics collection in internal/agent/opencode.go to track when structured output is missing during streaming responses.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →