BoundaryML/baml · error

Depth limit reached. Likely a circular reference.

Error message

Depth limit reached. Likely a circular reference.

What it means

parse_func increments a depth counter on every recursive parse attempt and aborts when depth exceeds 100. This guards against pathological recursive input (e.g. deeply nested or self-referential structures) causing stack exhaustion; the message suggests the input looked like a circular reference.

Source

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

use baml_types::CompletionState;

use super::ParseOptions;
use crate::jsonish::{
    parser::{
        fixing_parser,
        markdown_parser::{self, MarkdownResult},
        multi_json_parser,
    },
    value::Fixes,
    Value,
};

pub(super) fn parse_func(str: &str, mut options: ParseOptions, is_done: bool) -> Result<Value> {
    log::debug!("Parsing:\n{options:?}\n-------\n{str}\n-------");

    options.depth += 1;
    if options.depth > 100 {
        return Err(anyhow::anyhow!(
            "Depth limit reached. Likely a circular reference."
        ));
    }

    match serde_json::from_str(str) {
        Ok(mut v) => {
            match &mut v {
                Value::String(_, completion_state) => {
                    // The string must have been contained in quotes in order
                    // to parse as a JSON string, therefore it is complete.
                    *completion_state = CompletionState::Complete;
                }
                Value::Number(_, completion_state) => {
                    *completion_state = CompletionState::Incomplete;
                }
                Value::Boolean(_) => {}
                Value::Object(_, _) => {}
                Value::Array(_, _) => {}

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Inspect the raw input for runaway nested brackets or repeated prefixes and truncate/clean it before parsing
  2. Retry the LLM generation with instructions to emit flat, valid JSON
  3. If legitimately deep input is expected, raise the depth limit in ParseOptions/entry.rs
  4. Catch the error and fall back to a lenient extractor (e.g. markdown parser or regex extraction)
Defensive patterns

Strategy: validation

Validate before calling

fn nesting_depth(s: &str) -> usize {
    let mut d = 0usize; let mut max = 0usize;
    for c in s.chars() { match c { '{' | '[' => { d += 1; max = max.max(d); } '}' | ']' => d = d.saturating_sub(1), _ => {} } }
    max
}
// reject if nesting_depth(input) > 60

Try / catch

match parse(input) {
    Err(e) if e.to_string().contains("Depth limit") => retry_flat_generation(),
    other => other,
}

Prevention

When it happens

Trigger: Calling jsonish parse/parse_func (directly or via from_str) with input whose repair attempts recurse more than 100 levels — extremely deeply nested arrays/objects or a fixer that repeatedly re-wraps content.

Common situations: LLM output containing hundreds of nested brackets (e.g. model loops generating '[[' repetition), or parser repair heuristics feeding their own output back in.

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/e400d8aa1981b079. Report an issue: GitHub.