Lesson

Logs, Errors, and Debugging

Where logs go, what to do when jobs fail, how to diagnose.

Jobs will fail. The LLM will time out. The API key will expire. The run script will have a typo. The question isn't whether things break — it's how fast you find out why.

Where Logs Live

Every job gets its own log files:

workdir/logs/{job-name}/stdout.log
workdir/logs/{job-name}/stderr.log

Stdout is your job's output. Stderr is where errors go. The CLI wraps both:

bun run src/cli.ts logs hello-world

The Doctor

jobs doctor runs a systematic check on every job:

bun run src/cli.ts doctor

It checks: schedule file valid? Run script exists and is executable? Shebang present? Plist validates? Job loaded in launchd? Exit code non-zero? Stderr has content?

Each issue comes with a severity and a fix:

{
  "severity": "error",
  "message": "Run script is not executable",
  "fix": "chmod 755 system-jobs/my-job/run"
}

Doctor a specific job:

bun run src/cli.ts doctor hello-world

Common Failures

"env: bun: No such file or directory" in stderr

launchd runs with a minimal PATH. The manager injects your PATH into the plist, but if you installed bun AFTER the last sync, re-sync:

bun run sync

Non-zero exit code but no stderr

The job errored but swallowed the error. Add error handling to your run script:

#!/bin/bash
set -euo pipefail  # Exit on error, undefined vars, pipe failures

Or in bun:

main().catch((error) => {
  console.error("[job] Failed:", error);
  process.exit(1);
});

Job doesn't run on schedule

# Is it loaded?
launchctl list | grep aijs

# Force-run it
bun run src/cli.ts kick my-job

# Did it produce output?
bun run src/cli.ts logs my-job

If kick works but the schedule doesn't fire, check the schedule format. A common mistake: {"Hour": 9} without "type": "scheduled" — the parser might not infer the type correctly.

The Debugging Workflow

  1. jobs doctor <name> — systematic check
  2. jobs logs <name> — what did it output?
  3. jobs kick <name> — can it run at all?
  4. jobs plist <name> — is the generated plist correct?
  5. launchctl list | grep <label> — does launchd know about it?

This sequence finds 95% of problems. The other 5% is usually a run script bug — test your script directly with bun run system-jobs/my-job/run before relying on launchd.

What You Learned

  • Every job gets stdout.log and stderr.log in workdir/logs/
  • jobs doctor is the first thing to run when something breaks
  • PATH issues are the #1 launchd gotcha — re-sync after installing new tools
  • Test your run script directly before relying on launchd to run it