garrytan/gstack · error · Error
{{SECTION:id}} requires a section id
Error message
{{SECTION:id}} requires a section id What it means
The SECTION resolver in scripts/resolvers/sections.ts:58 requires at least one colon argument (the section id). Thrown when {{SECTION}} or {{SECTION:}} appears in a template. After validation it delegates to findSection and emits a pointer on Claude or inlines content on other hosts.
Source
Thrown at scripts/resolvers/sections.ts:58
return JSON.parse(raw) as SectionManifest;
}
function findSection(skill: string, id: string): SectionEntry {
const entry = loadManifest(skill).sections.find(s => s.id === id);
if (!entry) {
throw new Error(`{{SECTION:${id}}} — no section "${id}" in ${skill}/sections/manifest.json`);
}
return entry;
}
/**
* {{SECTION:id}} — pointer on Claude, inline on other hosts.
* Claude path uses the stable gstack-root install (`{skillRoot}/{skill}/sections/`),
* which always exists, instead of a naked relative path (Codex outside-voice #7).
*/
export const SECTION: ResolverFn = (ctx: TemplateContext, args?: string[]): string => {
const id = args?.[0];
if (!id) throw new Error('{{SECTION:id}} requires a section id');
const entry = findSection(ctx.skillName, id);
if (ctx.host === 'claude') {
const sectionPath = `${ctx.paths.skillRoot}/${ctx.skillName}/sections/${entry.file}`;
return [
`> **STOP.** Before ${entry.trigger}, Read \`${sectionPath}\` and execute it`,
`> in full. Do not work from memory — that section is the source of truth for this step.`,
].join('\n');
}
// Non-Claude hosts inline the section template content (monolith preserved).
// Inner {{RESOLVER}} tokens are expanded by the generator's multi-pass resolve.
const tmplPath = path.join(ROOT, ctx.skillName, 'sections', `${entry.file}.tmpl`);
return fs.readFileSync(tmplPath, 'utf-8').trimEnd();
};
/**
* {{SECTION_INDEX:skill}} — situation→section table from the passive manifest.View on GitHub (pinned to 94993f7401)
Solutions
- Provide the id: `{{SECTION:onboarding}}`
Example fix
<!-- before -->
{{SECTION}}
<!-- after -->
{{SECTION:onboarding}} Defensive patterns
Strategy: validation
Validate before calling
if (/\{\{SECTION:?\}\}/.test(tmplContent)) {
console.error(`{{SECTION}} requires an id in ${relTmplPath}`);
} Prevention
- Always pair SECTION with a colon and id
- Lint .tmpl files for empty-argument placeholders
- Copy the canonical form {{SECTION:onboarding}} from working skills
When it happens
Trigger: Typing {{SECTION}} without an id. Trimming a placeholder and dropping the argument. Misremembering the placeholder syntax.
Common situations: Hand-editing a .tmpl and forgetting the argument. Copy-paste from docs that omitted the id.
Related errors
- {{SECTION:${id}}} — no section "${id}" in ${skill}/sections/
- Unknown placeholder {{${resolverName}}} in ${relTmplPath}
- Unresolved placeholders in ${relTmplPath}: ${remaining.join(
- {{INVOKE_SKILL}} requires a skill name, e.g. {{INVOKE_SKILL:
- {{LEARNINGS_SEARCH:query=...}} value must match ${QUERY_SAFE
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/dfca112c8837dde7.
Report an issue: GitHub.