garrytan/gstack · error · Error

Skill "${name}" tests failed (exit ${exitCode}).\n${stderr}

Error message

Skill "${name}" tests failed (exit ${exitCode}).\n${stderr}

What it means

Thrown by handleTest after Bun.spawn(['bun','test',testFile]) exited with non-zero. The skill was found, the test file existed, and bun ran it — the test suite itself failed. The error message includes the exit code and up to the full stderr captured from the bun test process (no truncation in handleTest, unlike handleRun's 4096-byte cap).

Source

Thrown at browse/src/browser-skill-commands.ts:198

  const skill = readBrowserSkill(name, tiers);
  if (!skill) throw new Error(`Skill "${name}" not found.`);

  const testFile = path.join(skill.dir, 'script.test.ts');
  if (!fs.existsSync(testFile)) {
    throw new Error(`Skill "${name}" has no script.test.ts at ${testFile}`);
  }

  const proc = Bun.spawn(['bun', 'test', testFile], {
    cwd: skill.dir,
    stdout: 'pipe',
    stderr: 'pipe',
    env: process.env,
  });
  const exitCode = await proc.exited;
  const stdout = proc.stdout ? await new Response(proc.stdout).text() : '';
  const stderr = proc.stderr ? await new Response(proc.stderr).text() : '';
  if (exitCode !== 0) {
    throw new Error(`Skill "${name}" tests failed (exit ${exitCode}).\n${stderr}`);
  }
  return stderr || stdout || `tests passed for "${name}"`;
}

// ─── rm ─────────────────────────────────────────────────────────

function handleRm(args: string[], ctx: SkillCommandContext): string {
  const name = args[0];
  if (!name) throw new Error('Usage: $B skill rm <name> [--global]');
  const isGlobal = args.includes('--global');
  const tier: 'project' | 'global' = isGlobal ? 'global' : 'project';

  const tiers = ctx.tiers ?? defaultTierPaths();
  // For UX: if no project tier exists at all, default to global.
  const effectiveTier: 'project' | 'global' = (tier === 'project' && !tiers.project) ? 'global' : tier;

  const dst = tombstoneBrowserSkill(name, effectiveTier, tiers);
  return `Tombstoned "${name}" (${effectiveTier} tier) → ${dst}\n`;

View on GitHub (pinned to 94993f7401)

Solutions

  1. Read the stderr in the error message — it contains the failing test names and assertion diffs.
  2. Run the test directly for a full stack trace: `bun test <skill.dir>/script.test.ts`.
  3. If the failure is environment-related, ensure bun and any skill deps are on PATH (the test proc inherits process.env, unlike untrusted skill spawns).
  4. For selector/site drift, re-record the skill via /skillify.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  await handleSkillCommand(['test', name], ctx);
} catch (err: any) {
  if (/tests failed/.test(err.message)) {
    // err.message contains exit code + full stderr
    console.error(err.message);
    process.exitCode = err.exitCode ?? 1;
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: handleTest for a skill whose script.test.ts contains failing assertions, throws, or errors at import time. Exit code is bun test's: 1 for assertion failures, higher for crashes. The spawned process inherits process.env, so PATH-dependent imports (e.g. playwright) can also cause non-zero exit.

Common situations: Skill's Playwright script drifted from the live site so selectors no longer match; an assertion regressed after a dependency upgrade; bun or playwright not on PATH in the test environment; test fixtures missing; test depends on network and the host changed.

Related errors


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