{"record":{"id":"ef7c8fc42816717e","repo":"garrytan/gstack","slug":"skill-name-tests-failed-exit-exitcode-n","errorCode":null,"errorMessage":"Skill \"${name}\" tests failed (exit ${exitCode}).\\n${stderr}","messagePattern":"Skill \"(.+?)\" tests failed \\(exit (.+?)\\)\\.\\\\n(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"browse/src/browser-skill-commands.ts","lineNumber":198,"sourceCode":"  const skill = readBrowserSkill(name, tiers);\n  if (!skill) throw new Error(`Skill \"${name}\" not found.`);\n\n  const testFile = path.join(skill.dir, 'script.test.ts');\n  if (!fs.existsSync(testFile)) {\n    throw new Error(`Skill \"${name}\" has no script.test.ts at ${testFile}`);\n  }\n\n  const proc = Bun.spawn(['bun', 'test', testFile], {\n    cwd: skill.dir,\n    stdout: 'pipe',\n    stderr: 'pipe',\n    env: process.env,\n  });\n  const exitCode = await proc.exited;\n  const stdout = proc.stdout ? await new Response(proc.stdout).text() : '';\n  const stderr = proc.stderr ? await new Response(proc.stderr).text() : '';\n  if (exitCode !== 0) {\n    throw new Error(`Skill \"${name}\" tests failed (exit ${exitCode}).\\n${stderr}`);\n  }\n  return stderr || stdout || `tests passed for \"${name}\"`;\n}\n\n// ─── rm ─────────────────────────────────────────────────────────\n\nfunction handleRm(args: string[], ctx: SkillCommandContext): string {\n  const name = args[0];\n  if (!name) throw new Error('Usage: $B skill rm <name> [--global]');\n  const isGlobal = args.includes('--global');\n  const tier: 'project' | 'global' = isGlobal ? 'global' : 'project';\n\n  const tiers = ctx.tiers ?? defaultTierPaths();\n  // For UX: if no project tier exists at all, default to global.\n  const effectiveTier: 'project' | 'global' = (tier === 'project' && !tiers.project) ? 'global' : tier;\n\n  const dst = tombstoneBrowserSkill(name, effectiveTier, tiers);\n  return `Tombstoned \"${name}\" (${effectiveTier} tier) → ${dst}\\n`;","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/garrytan/gstack/blob/94993f74012782fd94416dd44b8314f6363a13a4/browse/src/browser-skill-commands.ts#L180-L216","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","solutions":["Read the stderr in the error message — it contains the failing test names and assertion diffs.","Run the test directly for a full stack trace: `bun test <skill.dir>/script.test.ts`.","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).","For selector/site drift, re-record the skill via /skillify."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  await handleSkillCommand(['test', name], ctx);\n} catch (err: any) {\n  if (/tests failed/.test(err.message)) {\n    // err.message contains exit code + full stderr\n    console.error(err.message);\n    process.exitCode = err.exitCode ?? 1;\n  } else {\n    throw err;\n  }\n}","preventionTips":["Run `bun test <skill.dir>/script.test.ts` directly for a full interactive trace.","Pin skill dependencies (playwright, bun version) so upgrades don't silently break selectors.","Treat test failures as site-drift signals — re-record via /skillify when selectors change."],"tags":["testing","bun","skill-management","gstack"],"backgroundTag":null,"analyzedSha":"94993f74012782fd94416dd44b8314f6363a13a4","analyzedAt":"2026-08-12T04:06:23.140Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}