{"record":{"id":"a042825c44114fa5","repo":"affaan-m/ECC","slug":"unknown-template-variable-key","errorCode":null,"errorMessage":"Unknown template variable: ${key}","messagePattern":"Unknown template variable: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/tmux-worktree-orchestrator.js","lineNumber":23,"sourceCode":"const { spawnSync } = require('child_process');\n\nfunction slugify(value, fallback = 'worker') {\n  const normalized = String(value || '')\n    .trim()\n    .toLowerCase()\n    .replace(/[^a-z0-9]+/g, '-')\n    .replace(/^-+|-+$/g, '');\n  return normalized || fallback;\n}\n\nfunction renderTemplate(template, variables) {\n  if (typeof template !== 'string' || template.trim().length === 0) {\n    throw new Error('launcherCommand must be a non-empty string');\n  }\n\n  return template.replace(/\\{([a-z_]+)\\}/g, (match, key) => {\n    if (!(key in variables)) {\n      throw new Error(`Unknown template variable: ${key}`);\n    }\n    return String(variables[key]);\n  });\n}\n\nfunction shellQuote(value) {\n  return `'${String(value).replace(/'/g, `'\\\\''`)}'`;\n}\n\nfunction formatCommand(program, args) {\n  return [program, ...args.map(shellQuote)].join(' ');\n}\n\nfunction buildTemplateVariables(values) {\n  return Object.entries(values).reduce((accumulator, [key, value]) => {\n    const stringValue = String(value);\n    const quotedValue = shellQuote(stringValue);\n","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/tmux-worktree-orchestrator.js#L5-L41","documentation":"Thrown by renderTemplate() when the launcherCommand template references a {placeholder} that is not present in the variables map. The orchestrator only populates a fixed set of variables per worker (branch_name, handoff_file, repo_root, session_name, status_file, task_file, worker_name, worker_slug, worktree_path, plus their _raw and _sh variants built by buildTemplateVariables), so any other token is treated as a typo.","triggerScenarios":"config.launcherCommand = 'cd {worktree} && claude' (worktree vs worktree_path); using {task} instead of {task_file}; using {repo} instead of {repo_root}; using an uppercase token like {Session_name} (the regex only matches [a-z_]); referencing a variable that exists on a different worker.","commonSituations":"Documentation drift: an example uses an old variable name; a user invents a placeholder assuming it will be passed through; case-sensitivity issues; a template copied from a different orchestrator.","solutions":["Use only the supported tokens: branch_name, handoff_file, repo_root, session_name, status_file, task_file, worker_name, worker_slug, worktree_path (plus _raw and _sh suffixes).","Check the regex in renderTemplate: only lowercase letters and underscores inside { } are interpolated, anything else is left as-is and will not trigger this error — but a typo'd lowercase token will.","If you need a custom value, pre-compute it and substitute before passing the template, or extend buildTemplateVariables in scripts/lib/tmux-worktree-orchestrator.js.","Print Object.keys(templateVariables) in a debug run to see exactly what is available."],"exampleFix":"// before\nlauncherCommand: 'claude --task {task} --cwd {worktree}'\n// -> Unknown template variable: task\n\n// after\nlauncherCommand: 'claude --task-file {task_file} --cwd {worktree_path}'","handlingStrategy":"validation","validationCode":"const ALLOWED = ['branch_name','handoff_file','repo_root','session_name','status_file','task_file','worker_name','worker_slug','worktree_path'];\n\nfunction validateLauncherTemplate(tpl) {\n  const used = [...tpl.matchAll(/\\{([a-z_]+)\\}/g)].map(m => m[1]);\n  const unknown = used.filter(k => !ALLOWED.includes(k) && !ALLOWED.some(a => k === `${a}` || k === `${a}_raw` || k === `${a}_sh`));\n  if (unknown.length) throw new Error(`Unknown template tokens: ${unknown.join(', ')}`);\n}\n\nvalidateLauncherTemplate(config.launcherCommand);","typeGuard":"const ALLOWED = ['branch_name','handoff_file','repo_root','session_name','status_file','task_file','worker_name','worker_slug','worktree_path'];\n\nfunction usesOnlyKnownTokens(tpl) {\n  return ![...tpl.matchAll(/\\{([a-z_]+)\\}/g)].map(m => m[1])\n    .some(k => !ALLOWED.includes(k.replace(/_(raw|sh)$/, '')));\n}","tryCatchPattern":"try {\n  buildOrchestrationPlan(config);\n} catch (error) {\n  if (/Unknown template variable/.test(error.message)) {\n    // surface the list of allowed variables to the user\n    throw new Error(`${error.message}. Allowed: ${ALLOWED.join(', ')}`);\n  }\n  throw error;\n}","preventionTips":["Pin template tokens to the documented set: branch_name, handoff_file, repo_root, session_name, status_file, task_file, worker_name, worker_slug, worktree_path (plus _raw and _sh suffixes).","Lint launcher templates in CI against the allowed-token list.","Remember tokens are case-sensitive and lowercase-only — {Session_name} is left as-is, not interpolated.","When in doubt, print Object.keys(templateVariables) in a dry-run."],"tags":["orchestration","tmux","templating","validation"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}