BoundaryML/baml · error

Failed to parse JSON

Error message

Failed to parse JSON

What it means

parse_func tries serde_json first, then a series of fallback/fixed parsers, and if every strategy fails to produce a Value it returns a generic 'Failed to parse JSON'. It is the terminal failure of the whole jsonish parsing pipeline for the given string.

Source

Thrown at engine/baml-lib/jsonish/src/jsonish/parser/entry.rs:237

            }
            Err(e) => {
                log::debug!("Error fixing json: {e:?}");
            }
        }
    }

    if options.allow_as_string {
        return Ok(Value::String(
            str.to_string(),
            if is_done {
                CompletionState::Complete
            } else {
                CompletionState::Incomplete
            },
        ));
    }

    Err(anyhow::anyhow!("Failed to parse JSON"))
}

pub fn parse(str: &str, options: ParseOptions, is_done: bool) -> Result<Value> {
    let res = parse_func(str, options, is_done)?;
    Ok(res.simplify(is_done))
}

#[cfg(test)]
mod tests {
    use baml_types::CompletionState;

    use super::*;
    use crate::jsonish::Value;

    fn to_any_of(inner: Value, s: &str) -> Value {
        Value::AnyOf(vec![inner], s.to_string())
    }

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Log the raw input string and confirm it actually contains JSON-like content
  2. Check log::debug output ('Parsing: ...') to see which strategies were attempted
  3. Retry the generation with an explicit JSON schema/format instruction
  4. Add custom deserializers or use a markdown/code-fence wrapper so the content reaches the right parser
  5. Handle the Err branch with a user-facing retry instead of panicking

Example fix

// before
let v = jsonish::from_str(&target, llm_output)?;
// after
let v = match jsonish::from_str(&target, llm_output) {
    Ok(v) => v,
    Err(e) => { tracing::error!("unparseable output: {llm_output:?}"); return Err(e.into()); }
};
Defensive patterns

Strategy: try-catch

Validate before calling

fn has_json_shape(s: &str) -> bool { let t = s.trim(); t.starts_with('{') || t.starts_with('[') || t.contains("```") }

Try / catch

match jsonish::from_str(&t, out) {
    Ok(v) => v,
    Err(e) if e.to_string() == "Failed to parse JSON" => {
        tracing::warn!("no JSON in output: {out:.200}");
        retry_with_format_instruction()
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: Passing a string to jsonish parse (or from_str) that neither serde_json nor any repair heuristic (fixing parser, markdown, multi-json, custom deserializers) can turn into a Value.

Common situations: LLM returned free-form prose with no JSON at all, an empty response, or output so malformed that even partial-repair strategies fail.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/26f92635eb0a2a44. Report an issue: GitHub.