Troubleshoots Outfitter Stack issues including Result handling, MCP problems, CLI output, exit codes, and logging. Use when debugging stack-specific issues, unexpected errors, wrong output modes, or when "debug Result", "MCP not working", "wrong exit code", or "logging issue" are mentioned.
Troubleshoot @outfitter/* package issues.
Symptom: Result is err when it should be ok.
Check validation:
const inputResult = validateInput(rawInput);
if (inputResult.isErr()) {
console.log("Validation failed:", inputResult.error.details);
return inputResult;
}
Check async:
// BAD: Missing await
const result = getUser(id); // Promise, not Result!
// GOOD
const result = await getUser(id);
Symptom: TypeScript doesn't know type after isOk().
// BAD: Reassigning breaks narrowing
let result = await getUser(id);
if (result.isOk()) {
result = await updateUser(result.value); // Breaks!
}
// GOOD: Separate variables
const getResult = await getUser(id);
if (getResult.isErr()) return getResult;
const updateResult = await updateUser(getResult.value);
Use _tag for narrowing:
if (result.isErr()) {
switch (result.error._tag) {
case "ValidationError":
console.log(result.error.details);
break;
case "NotFoundError":
console.log(result.error.resourceId);
break;
}
}
Register before start():
server.registerTool(myTool);
server.start(); // After registration!
Check schema is valid Zod with .describe():
const schema = z.object({
query: z.string().describe("Required for AI"),
});
Verify handler is async:
handler: async (input) => { // Not sync!
return Result.ok(data);
}
Return Result, not raw value:
// BAD
return { data: "value" };
// GOOD
return Result.ok({ data: "value" });
Force mode:
await output(data, { mode: "json" });
Check environment:
OUTFITTER_JSON=1 myapp list
OUTFITTER_JSON=0 myapp list --json # Forces human!
Await output:
// BAD
output(data);
process.exit(0); // May exit before output!
// GOOD
await output(data);
Use exitWithError:
// BAD
process.exit(1);
// GOOD
exitWithError(result.error);
Exit code table:
| Category | Exit | |----------|------| | validation | 1 | | not_found | 2 | | conflict | 3 | | permission | 4 | | timeout | 5 | | rate_limit | 6 | | network | 7 | | internal | 8 | | auth | 9 | | cancelled | 130 |
const logger = createLogger({
redaction: { enabled: true }, // Must be true!
});
// Custom patterns
const logger = createLogger({
redaction: {
enabled: true,
patterns: ["password", "apiKey", "myCustomSecret"],
},
});
import { createChildLogger } from "@outfitter/logging";
const requestLogger = createChildLogger(ctx.logger, {
requestId: ctx.requestId,
handler: "myHandler",
});
requestLogger.info("Processing", { data }); // Includes requestId
const logger = createLogger({
level: process.env.LOG_LEVEL || "info",
});
// Hierarchy: trace < debug < info < warn < error < fatal
// "info" hides trace and debug
function traceResult<T, E>(name: string, result: Result<T, E>): Result<T, E> {
console.log(`[${name}]`, result.isOk() ? "OK:" : "ERR:",
result.isOk() ? result.value : result.error);
return result;
}
const result = traceResult("getUser", await getUser(id));
console.log("Context:", {
requestId: ctx.requestId,
hasLogger: !!ctx.logger,
hasConfig: !!ctx.config,
hasSignal: !!ctx.signal,
cwd: ctx.cwd,
});
const parseResult = schema.safeParse(rawInput);
if (!parseResult.success) {
console.log("Zod errors:", parseResult.error.issues);
}
outfitter-stack:stack-patterns — Correct patternsoutfitter-stack:stack-review — Systematic auditSearch for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer