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

  1. Run `$B skill list` and copy the exact name.
  2. Verify the skill directory exists: `ls ~/.gstack/browser-skills/<name>/` and `ls <project>/.gstack/browser-skills/<name>/`.
  3. Confirm SKILL.md is present and parses (has `name` and `host`).
  4. 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

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


AI-assisted analysis of garrytan/gstack@94993f7401 (2026-08-12). Data as JSON: /api/errors/71d23e1e7dba5df0. Report an issue: GitHub.