garrytan/gstack · error · Error
{{INVOKE_SKILL}} requires a skill name, e.g. {{INVOKE_SKILL:
Error message
{{INVOKE_SKILL}} requires a skill name, e.g. {{INVOKE_SKILL:plan-ceo-review}} What it means
generateInvokeSkill() in scripts/resolvers/composition.ts:13 requires a non-empty skill name as the first colon argument to {{INVOKE_SKILL:skill-name}}. Thrown when the placeholder appears without an argument ({{INVOKE_SKILL}} or {{INVOKE_SKILL:}}) in a .tmpl. Optional skip= arguments are parsed from args[1+].
Source
Thrown at scripts/resolvers/composition.ts:13
import type { TemplateContext } from './types';
/**
* {{INVOKE_SKILL:skill-name}} — emits prose instructing Claude to read
* another skill's SKILL.md and follow it, skipping preamble sections.
*
* Supports optional skip= parameter for additional sections to skip:
* {{INVOKE_SKILL:plan-ceo-review:skip=Outside Voice,Design Outside Voices}}
*/
export function generateInvokeSkill(ctx: TemplateContext, args?: string[]): string {
const skillName = args?.[0];
if (!skillName || skillName === '') {
throw new Error('{{INVOKE_SKILL}} requires a skill name, e.g. {{INVOKE_SKILL:plan-ceo-review}}');
}
// Parse optional skip= parameter from args[1+]
const extraSkips = (args?.slice(1) || [])
.filter(a => a.startsWith('skip='))
.flatMap(a => a.slice(5).split(','))
.map(s => s.trim())
.filter(Boolean);
const DEFAULT_SKIPS = [
'Preamble (run first)',
'AskUserQuestion Format',
'Completeness Principle — Boil the Ocean',
'Search Before Building',
'Contributor Mode',
'Completion Status Protocol',
'Telemetry (run last)',
'Step 0: Detect platform and base branch',View on GitHub (pinned to 94993f7401)
Solutions
- Provide the skill name: `{{INVOKE_SKILL:plan-ceo-review}}`
- Use the optional skip= parameter for additional sections: `{{INVOKE_SKILL:foo:skip=A,B}}`
Example fix
<!-- before -->
{{INVOKE_SKILL}}
<!-- after -->
{{INVOKE_SKILL:plan-ceo-review}} Defensive patterns
Strategy: validation
Validate before calling
if (/\{\{INVOKE_SKILL(?::[^}]*)?\}\}/.test(tmplContent)) {
for (const m of tmplContent.matchAll(/\{\{(INVOKE_SKILL:?[^}]*)\}\}/g)) {
const arg = m[1].split(':')[1];
if (!arg) console.error(`{{INVOKE_SKILL}} needs a name in ${relTmplPath}`);
}
} Prevention
- Always pair INVOKE_SKILL with a colon and skill name
- Lint .tmpl files for empty-argument placeholders
- Copy the canonical form {{INVOKE_SKILL:plan-ceo-review}} from working skills
When it happens
Trigger: Typing `{{INVOKE_SKILL}}` without the colon and name. Copy-paste dropping the name. Trimming a placeholder and accidentally removing the argument.
Common situations: Hand-editing a .tmpl and forgetting the argument. Misremembering the placeholder syntax.
Related errors
- Unknown placeholder {{${resolverName}}} in ${relTmplPath}
- Unresolved placeholders in ${relTmplPath}: ${remaining.join(
- {{LEARNINGS_SEARCH:query=...}} value must match ${QUERY_SAFE
- Invalid preamble-tier: ${tier} in ${ctx.tmplPath}. Must be 1
- {{SECTION:${id}}} — no section "${id}" in ${skill}/sections/
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/73554c9ccdab7c55.
Report an issue: GitHub.