{"record":{"id":"a0acabd1f5e5e050","repo":"coleam00/Archon","slug":"unknown-input-inputs-name-suffix-a0acab","errorCode":null,"errorMessage":"Unknown input '$INPUTS.${name}'.${suffix}","messagePattern":"Unknown input '\\$INPUTS\\.(.+?)'\\.(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/workflows/src/output-ref.ts","lineNumber":173,"sourceCode":"}\n\n/** Resolve every runtime `$INPUTS.<name>` reference in a text surface. */\nexport function substituteInputRefs(\n  text: string,\n  inputs: Record<string, JsonValue> | undefined\n): string {\n  const pattern = new RegExp(String.raw`\\$INPUTS\\.(${INPUT_NAME_SOURCE})`, 'g');\n  return text.replace(pattern, (_match, name: string) => {\n    if (inputs && Object.hasOwn(inputs, name)) return canonicalValueText(inputs[name]);\n    const known = inputs ? Object.keys(inputs) : [];\n    const hint = similarNodeIds(name, known);\n    const suffix =\n      hint.length > 0\n        ? ` Did you mean ${hint.map(candidate => `$INPUTS.${candidate}`).join(', ')}?`\n        : known.length > 0\n          ? ` Available inputs: ${known.map(candidate => `$INPUTS.${candidate}`).join(', ')}.`\n          : ' This run has no declared inputs.';\n    throw new Error(`Unknown input '$INPUTS.${name}'.${suffix}`);\n  });\n}\n\n/** Anchored whole-value form: the ENTIRE (trimmed) string is one `$id.output[.field]` ref. */\nconst WHOLE_OUTPUT_REF_PATTERN = new RegExp(\n  `^${OUTPUT_REF_SOURCE}(?:\\\\.([a-zA-Z_][a-zA-Z0-9_]*))?$`\n);\n\n/**\n * Parse a string that is exactly one whole `$node.output[.field]` reference\n * (after trimming), or undefined when it is anything else — a literal, a\n * template with surrounding text, or not a ref at all. This is what lets a\n * binding value distinguish \"pass the logical value through\" from \"splice text\n * into a template\" without inventing a second ref grammar.\n */\nexport function parseWholeOutputRef(text: string): { nodeId: string; field?: string } | undefined {\n  const m = WHOLE_OUTPUT_REF_PATTERN.exec(text.trim());\n  if (!m) return undefined;","sourceCodeStart":155,"sourceCodeEnd":191,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/workflows/src/output-ref.ts#L155-L191","documentation":"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).","triggerScenarios":"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).","commonSituations":"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.","solutions":["Declare the missing input in the workflow's `inputs:` block.","Or fix the reference spelling to a declared input (use the 'Did you mean'/Available inputs hint in the message).","Or remove the `$INPUTS.` reference and pass the value another way (node body literal or producer output ref)."],"exampleFix":"// before\ninputs: []\nnode: echo '$INPUTS.target'\n// after\ninputs:\n  - name: target\n    type: string","handlingStrategy":"validation","validationCode":"function assertInputsReferenced(workflow: Workflow): void {\n  const declared = new Set(workflow.inputs?.map(i => i.name));\n  const body = JSON.stringify(workflow.nodes);\n  for (const m of body.matchAll(/\\$INPUTS\\.([a-zA-Z_][a-zA-Z0-9_]*)/g)) {\n    if (!declared.has(m[1])) throw new Error(`Node body references undeclared input $INPUTS.${m[1]}`);\n  }\n}","typeGuard":"function referencesDeclaredInput(workflow: Workflow, name: string): boolean {\n  return (workflow.inputs ?? []).some(i => i.name === name);\n}","tryCatchPattern":"try {\n  const body = substituteInputRefs(node.body, inputs);\n} catch (err) {\n  if ((err as Error).message.includes('Unknown input')) {\n    // message lists 'Did you mean' candidates — surface to the author\n    console.error(err.message);\n  } else throw err;\n}","preventionTips":["Keep `inputs:` declarations adjacent to the nodes that consume them and review both together.","Rename inputs with a repo-wide search of `$INPUTS.<old>` occurrences.","Add a lint/static check that every `$INPUTS.x` matches a declared input.","Run a dry-run/validation pass on workflows after editing inputs."],"tags":["workflow","inputs","validation"],"backgroundTag":"unknown-input-reference","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}