Skip to content

BUG: {{steps.X.output}} renders as empty string silently — worker receives blank prompt #40

Description

@addadi

Bug: {{steps.X.output}} renders as empty string silently — worker receives blank prompt

Summary

In the state-based template engine used for prompt_template rendering, any unresolvable expression — including {{steps.X.output}} — silently renders as an empty string. The prompt is dispatched to the worker empty, and the worker generates output from nothing (or hallucinates from residual session context). There is no error, no failed step, no log line pointing at the missing variable.

Root cause

src/templates.zig:401 resolveNewExpression handles state.*, input.*, item, item.*, config.*, store.* — but has no steps. prefix handler, and ends with:

// Unknown expression — return empty
return alloc.dupe(u8, "") catch return error.OutOfMemory;

(src/templates.zig:508)

The old graph-engine path (resolveStepRef, src/templates.zig:238) does handle steps. via ctx.step_outputs, but the runtime path used by executeTaskNode goes through renderWorkflowTemplate → the new engine (src/engine.zig:1148), so {{steps.X.output}} falls into the unknown-expression empty-string branch.

Every other miss in the same function (state.X missing key, input.X missing, item with no item_json) also returns "" silently.

Why this is dangerous

A typo'd or not-yet-populated variable produces a blank prompt instead of a failure. The step "succeeds", the run continues, and the garbage output propagates downstream. This is a correctness black hole: the failure mode is invisible at the orchestration layer.

Reproduction

  1. Create a 2-step sequential workflow. Step b has prompt_template: "Summarize: {{steps.a.output}}".
  2. Run it. Step b dispatches with prompt Summarize: — the {{steps.a.output}} expression vanishes.
  3. No error anywhere; step b completes against a malformed prompt.

Expected behavior

  • {{steps.X.Y}} should resolve against a per-step output registry (populated as steps complete), so {{steps.a.output}} yields the actual output of step a.
  • For prompt rendering (as opposed to {% if %} conditionals where empty-as-falsy is legitimate), an unresolvable reference should fail the node visibly (UnresolvedReference / StepNotFound) instead of rendering empty.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions