garrytan/gstack · error · Error
Skill "${name}" not found.
Error message
Skill "${name}" not found. What it means
Thrown by handleRun after readBrowserSkill returned null for the given name across all three tiers. Functionally identical to the show-path 'not found' error but with shorter wording (no 'in any tier' suffix). Same masking caveat applies: a malformed SKILL.md in every tier also resolves to null.
Source
Thrown at browse/src/browser-skill-commands.ts:150
let timeoutSeconds = DEFAULT_TIMEOUT_SECONDS;
for (let i = 0; i < args.length; i++) {
const a = args[i];
if (a.startsWith('--timeout=')) {
const n = parseInt(a.slice('--timeout='.length), 10);
if (!isNaN(n) && n > 0) timeoutSeconds = n;
continue;
}
passthrough.push(a);
}
return { passthrough, timeoutSeconds };
}
async function handleRun(args: string[], ctx: SkillCommandContext): Promise<string> {
const name = args[0];
if (!name) throw new Error('Usage: $B skill run <name> [--arg k=v]... [--timeout=Ns]');
const tiers = ctx.tiers ?? defaultTierPaths();
const skill = readBrowserSkill(name, tiers);
if (!skill) throw new Error(`Skill "${name}" not found.`);
const { passthrough, timeoutSeconds } = parseSkillRunArgs(args.slice(1));
const result = await spawnSkill({
skill,
skillArgs: passthrough,
trusted: skill.frontmatter.trusted,
timeoutSeconds,
port: ctx.port,
});
if (result.exitCode !== 0 || result.timedOut || result.truncated) {
const summary = result.truncated
? `truncated stdout at ${MAX_STDOUT_BYTES} bytes`
: result.timedOut
? `timed out after ${timeoutSeconds}s`
: `exit ${result.exitCode}`;
const err = new Error(`Skill "${name}" failed: ${summary}\n--- stderr ---\n${result.stderr.slice(0, 4096)}`);
(err as any).exitCode = result.exitCode || 1;View on GitHub (pinned to 94993f7401)
Solutions
- Run `$B skill list` and copy the exact name.
- Verify the skill directory exists: `ls ~/.gstack/browser-skills/<name>/` and `ls <project>/.gstack/browser-skills/<name>/`.
- Confirm SKILL.md is present and parses (has `name` and `host`).
- Check for a tombstone under <tier>/.tombstones/ and restore it if unintentional.
Defensive patterns
Strategy: validation
Validate before calling
import { readBrowserSkill, defaultTierPaths } from './browser-skills';
function assertSkillReadable(name: string, tiers = defaultTierPaths()): void {
if (!readBrowserSkill(name, tiers)) {
const suggestion = listBrowserSkills(tiers).map(s => s.name).join(', ');
throw new Error(`Skill "${name}" not found. Known: ${suggestion || '(none)'}`);
}
} Prevention
- Verify the skill resolves before constructing run args.
- Project tier is null outside a git repo — account for this in non-project contexts.
- Tombstones hide skills from list/run; check <tier>/.tombstones/ for removals.
When it happens
Trigger: handleRun with a name absent from project, global, and bundled tier directories; or present only as directories whose SKILL.md fails to parse (missing required `host` field). Distinguished from the spawn-time errors (script.ts missing, exit non-zero) by firing before spawnSkill is called.
Common situations: Name typo; skill tombstoned; running outside a git project so project tier is null; fresh install where the bundled skills dir resolved to the wrong path (detectBundledRoot fallback walked to the wrong parent); agent invoked run on a skill it just wrote via /skillify before commit completed.
Related errors
- Skill "${name}" not found in any tier.
- Skill "${opts.skill.name}" missing script.ts at ${scriptPath
- Skill "${name}" has no script.test.ts at ${testFile}
- Invalid file path in stageSkill: "${relPath}".
- commitSkill: tier "${opts.tier}" has no resolved path.
AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12).
Data as JSON: /api/errors/71d23e1e7dba5df0.
Report an issue: GitHub.