Diagnostics
Troubleshooting
Troubleshooting
Common errors, their causes, and fixes. Organized by where you see them; Foundry editor, validation API, runtime trace.
Validation errors (at save / validate time)
"stepId not found"
A field's from.stepId references a step that doesn't exist in the ladder. Fix: check spelling, or add the missing step.
# Wrong; 'draft' doesn't exist
fields:
- { name: Draft, type: ingest, from: { stepId: drft, loopRef: current } }"knob reference not found"
A {{knobs.X}} template references a knob not declared in the top-level knobs: map. Fix: declare the knob, or remove the reference.
"exit step does not exist"
The top-level exit: X names a step that isn't in the ladder. Fix: add the step, or change the exit.
"slate reference not found"
A slateRead or slateWrite references a slate title that isn't declared in the top-level slates: array. Fix: declare the slate, or fix the title.
"folder not found in slate"
A slateRead/slateWrite references a folder name not declared in the slate. Fix: add the folder to the slate, or fix the folder name.
"metatag not in effective schema"
A slateRead of metatag: X on a specific file requires X to be in that file's effective schema (folder-level ∪ file-level). Fix: declare the metatag at folder or file level, or read across the whole folder (omit file:; returns whatever files have it, possibly empty).
"condition: multiple condition keys found"
An if block has more than one condition key ({ equals: "1", contains: "x" }). Fix: only one condition per if. For compound needs, use jsonMatches with a richer schema.
"integerRange: expected exactly 2 elements"
integerRange must be [a, b] with exactly two values. Same for numberRange. Fix: pass a two-element array.
"executionBudget.maxSpend exceeds platform max"
Declared maxSpend > $50 (platform max). Fix: lower to ≤ $50. Same caps apply to maxHops (10k), maxLlmCalls (5k), maxRecursion (20).
"if: no condition key found"
An if: object has only then and/or else but no condition. Fix: add exactly one condition key (equals, integerEquals, integerRange, numberEquals, numberRange, contains, in, jsonMatches).
Runtime errors (in the trace)
Schema-matched write no-op
A schema-matched slateWrite ran but the LLM output didn't validate against match. This is a silent no-op — no error, no event. Causes:
- LLM didn't emit JSON (emitted prose instead).
- JSON emitted but missing required fields.
- JSON fields had wrong types or out-of-range values.
Fix: tighten the system prompt to demand exact JSON. Or relax the schema. Or switch to a gated write (if: { ... then: { write: ... } }) which writes unconditionally on gate match. Inspect the slate after execution to verify writes occurred.
Ceiling exhaustion
The execution hit a runtime cap (spend, hops, llmCalls, or recursion). The ladder returns best-so-far output. This is graceful termination, not an error. Fix: raise the relevant ceiling in executionBudget (up to the platform max), or restructure the ladder to use fewer calls.
Dynamic step validation failure
A dynamic step (dynamic: true) loaded a config that failed runtime validation. Causes:
- The planner LLM emitted a config that doesn't match the step schema.
- A field reference in the generated config doesn't resolve to the current manifest.
- The generated config contains
dynamic: true(nesting is disallowed).
Fix: tighten the planner's system prompt. Declare an onInvalid: fallback action (continue / abort / jump) on the dynamic step. Default is abort.
NodeDependencyError
A downstream step tried to ingest from an upstream step whose nodes were ALL pruned by a gate. Fix: loosen the gate condition, or add a fallback step that fires when the gate prunes everything (use else: { jump: ... } to redirect).
LLM output isn't valid JSON
The LLM didn't emit parseable JSON despite being asked to. Fix:
- Make the system prompt more explicit ("Output ONLY valid JSON, no prose, no code fences").
- Use
jsonMatchesgate to verify before consuming. - Use few-shot examples in the prompt showing the exact JSON shape expected.
Foundry editor issues
"YAML preview shows different content than the visual editor"
The visual editor is the source of truth; the YAML preview is generated from it. If you've hand-edited YAML and it diverges, click Re-sync from YAML to import the YAML back into the editor (this overwrites visual edits).
"I can't drag a step into a group"
Groups cannot be nested. A group step can contain only normal/sequential steps, not other groups. This is enforced at validation.
"My knob slider only shows one tick"
Slider knobs need at least 2 ticks in the steps array. Add more ticks, or switch the input to numerical for a continuous range.
"The timeline shows steps I didn't author"
Steps without timeline: circle are still part of the ladder and still execute; they just don't render as dots in Studio/Foundry's compact timeline. Mark noise steps with timeline: hidden if you want them to execute but stay invisible. (Note: in the current implementation only circle and init are valid timeline values; hidden is future.)
API + calling issues
404 on POST /v1/{author}/{name}/chat/completions
Either the ladder doesn't exist, the caller's API key doesn't have access, or the author/name in the URL is wrong. The API returns 404 (not 403) on access-denied to avoid leaking ladder existence.
"model not allowed"
The caller passed a model not in the ladder's allowedTargets.models allowlist. Fix: update the ladder to allow the model (allowedTargets: { strategy: universal } or add to the allowlist), or have the caller use an allowed model.
"insufficient_quota" (402)
The caller's daily free limit was reached. Fix: upgrade to a paid account, or wait for the daily reset.
Ladder times out
Ladders block for the duration of execution. A 5-node ToT with synthesis can take 30+ seconds. If your client has a 30s timeout, it'll abort mid-execution. Fix: use the async POST /v1/pipelines/instances endpoint for long-running ladders; returns immediately with an instance ID, poll for results.
OpenAI client library rejects the response
LADR ladder responses are byte-for-byte OpenAI-compatible. If your client rejects one, check:
- The
modelfield is set to{author}/{name}not the underlying model name; some clients validate this. - The
usagefield is populated with aggregate tokens across all calls in the ladder. - Streaming is not currently supported; ensure
stream: false(or omit).