coleam00/Archon · error · Error

Unknown input '$INPUTS.${name}'.${suffix}

Error message

Unknown input '$INPUTS.${name}'.${suffix}

What it means

substituteInputRefs replaces `$INPUTS.<name>` references in node bodies with declared run inputs. It throws when the referenced input name is not declared, appending either a closest-match 'Did you mean' hint or the list of available inputs (or a note that the run declares none).

Source

Thrown at packages/workflows/src/output-ref.ts:173

}

/** Resolve every runtime `$INPUTS.<name>` reference in a text surface. */
export function substituteInputRefs(
  text: string,
  inputs: Record<string, JsonValue> | undefined
): string {
  const pattern = new RegExp(String.raw`\$INPUTS\.(${INPUT_NAME_SOURCE})`, 'g');
  return text.replace(pattern, (_match, name: string) => {
    if (inputs && Object.hasOwn(inputs, name)) return canonicalValueText(inputs[name]);
    const known = inputs ? Object.keys(inputs) : [];
    const hint = similarNodeIds(name, known);
    const suffix =
      hint.length > 0
        ? ` Did you mean ${hint.map(candidate => `$INPUTS.${candidate}`).join(', ')}?`
        : known.length > 0
          ? ` Available inputs: ${known.map(candidate => `$INPUTS.${candidate}`).join(', ')}.`
          : ' This run has no declared inputs.';
    throw new Error(`Unknown input '$INPUTS.${name}'.${suffix}`);
  });
}

/** Anchored whole-value form: the ENTIRE (trimmed) string is one `$id.output[.field]` ref. */
const WHOLE_OUTPUT_REF_PATTERN = new RegExp(
  `^${OUTPUT_REF_SOURCE}(?:\\.([a-zA-Z_][a-zA-Z0-9_]*))?$`
);

/**
 * Parse a string that is exactly one whole `$node.output[.field]` reference
 * (after trimming), or undefined when it is anything else — a literal, a
 * template with surrounding text, or not a ref at all. This is what lets a
 * binding value distinguish "pass the logical value through" from "splice text
 * into a template" without inventing a second ref grammar.
 */
export function parseWholeOutputRef(text: string): { nodeId: string; field?: string } | undefined {
  const m = WHOLE_OUTPUT_REF_PATTERN.exec(text.trim());
  if (!m) return undefined;

View on GitHub (pinned to 0773b97458)

Solutions

  1. Declare the missing input in the workflow's `inputs:` block.
  2. Or fix the reference spelling to a declared input (use the 'Did you mean'/Available inputs hint in the message).
  3. Or remove the `$INPUTS.` reference and pass the value another way (node body literal or producer output ref).

Example fix

// before
inputs: []
node: echo '$INPUTS.target'
// after
inputs:
  - name: target
    type: string
Defensive patterns

Strategy: validation

Validate before calling

function assertInputsReferenced(workflow: Workflow): void {
  const declared = new Set(workflow.inputs?.map(i => i.name));
  const body = JSON.stringify(workflow.nodes);
  for (const m of body.matchAll(/\$INPUTS\.([a-zA-Z_][a-zA-Z0-9_]*)/g)) {
    if (!declared.has(m[1])) throw new Error(`Node body references undeclared input $INPUTS.${m[1]}`);
  }
}

Type guard

function referencesDeclaredInput(workflow: Workflow, name: string): boolean {
  return (workflow.inputs ?? []).some(i => i.name === name);
}

Try / catch

try {
  const body = substituteInputRefs(node.body, inputs);
} catch (err) {
  if ((err as Error).message.includes('Unknown input')) {
    // message lists 'Did you mean' candidates — surface to the author
    console.error(err.message);
  } else throw err;
}

Prevention

When it happens

Trigger: A node body contains `$INPUTS.foo` while the workflow's `inputs:` declaration has no `foo` (typo, renamed input, or the workflow declares no inputs at all).

Common situations: Renaming an input in `inputs:` without updating node bodies; copying a node from another workflow with different input names; running a workflow whose inputs section was deleted.

Related errors


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/a0acabd1f5e5e050. Report an issue: GitHub.