{"record":{"id":"4133e3c6af218a5b","repo":"garrytan/gstack","slug":"unknown-placeholder-resolvername-in-reltm","errorCode":null,"errorMessage":"Unknown placeholder {{${resolverName}}} in ${relTmplPath}","messagePattern":"Unknown placeholder (.+?)\\} in (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/gen-skill-docs.ts","lineNumber":690,"sourceCode":" */\nfunction resolvePlaceholders(\n  tmplContent: string,\n  ctx: TemplateContext,\n  hostConfig: HostConfig,\n  relTmplPath: string,\n): string {\n  // effectiveSuppressedResolvers() honors --respect-detection: when gbrain is\n  // detected locally, GBRAIN_* resolvers un-suppress. Shared by SKILL.md and\n  // section generation so both paths get the same gbrain-aware behavior.\n  const suppressed = effectiveSuppressedResolvers(hostConfig);\n  const onePass = (input: string): string =>\n    input.replace(/\\{\\{(\\w+(?::[^}]+)?)\\}\\}/g, (_match, fullKey) => {\n      const parts = fullKey.split(':');\n      const resolverName = parts[0];\n      const args = parts.slice(1);\n      if (suppressed.has(resolverName)) return '';\n      const entry = RESOLVERS[resolverName];\n      if (!entry) throw new Error(`Unknown placeholder {{${resolverName}}} in ${relTmplPath}`);\n      const { resolve, appliesTo } = unwrapResolver(entry);\n      if (appliesTo && !appliesTo(ctx)) return '';\n      return args.length > 0 ? resolve(ctx, args) : resolve(ctx);\n    });\n\n  // Multi-pass: a resolver may emit content that itself contains {{TOKENS}} — the\n  // {{SECTION:id}} resolver inlines a section template (with its own resolvers)\n  // for non-Claude hosts. .replace() doesn't re-scan inserted text, so loop until\n  // the output stabilizes. Bounded to avoid an infinite loop if a resolver ever\n  // emits its own placeholder; 6 passes is far more nesting than any skill needs.\n  let content = tmplContent;\n  for (let pass = 0; pass < 6; pass++) {\n    const next = onePass(content);\n    if (next === content) break;\n    content = next;\n  }\n\n  const remaining = content.match(/\\{\\{(\\w+(?::[^}]+)?)\\}\\}/g);","sourceCodeStart":672,"sourceCodeEnd":708,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/scripts/gen-skill-docs.ts#L672-L708","documentation":"Thrown by onePass() during template resolution in scripts/gen-skill-docs.ts:690 when a {{TOKEN}} or {{TOKEN:args}} placeholder is encountered whose resolver name is not in RESOLVERS and is not in the suppressed set. The error includes relTmplPath so you can locate the offending template.","triggerScenarios":"Typing `{{INVKOE_SKILL:foo}}` in a .tmpl. Referencing a resolver that was renamed. Adding a new resolver but forgetting to register it in the RESOLVERS map.","commonSituations":"Renaming a resolver without grepping all .tmpl files. Copying a token from documentation that is out of date or from a different skill family.","solutions":["Open relTmplPath and find the placeholder named in the message","Cross-check the resolver name against the RESOLVERS keys in scripts/resolvers/index","Fix the typo, or remove the placeholder if it is unused","If the placeholder is intentionally absent for this build, suppress it (e.g. via --respect-detection for gbrain-aware suppression)"],"exampleFix":"<!-- before (in some.tmpl) -->\nUse {{INVKOE_SKILL:plan-ceo-review}}\n<!-- after -->\nUse {{INVOKE_SKILL:plan-ceo-review}}","handlingStrategy":"validation","validationCode":"import { RESOLVERS } from './resolvers';\nconst known = new Set(Object.keys(RESOLVERS));\nfor (const m of tmplContent.matchAll(/\\{\\{(\\w+)/g)) {\n  if (!known.has(m[1]) && !suppressed.has(m[1])) {\n    console.error(`Unknown placeholder {{${m[1]}}} in ${relTmplPath}`);\n  }\n}","typeGuard":"import { RESOLVERS } from './resolvers';\nconst isKnownResolver = (name: string): boolean => name in RESOLVERS;","tryCatchPattern":null,"preventionTips":["Grep all .tmpl files when renaming a resolver","Keep resolver registry docs in sync with the RESOLVERS map","Run a dry gen:skill-docs build in CI to catch typos early"],"tags":["template","resolver","build"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}