[
  {
    "id": "cron-exit-137",
    "exit_code_or_signal": "137 (SIGKILL)",
    "name": "Killed by SIGKILL — OOM or cgroup limit",
    "meaning": "Process was terminated by SIGKILL (signal 9); 137 = 128 + 9. Cannot be caught or ignored; the process dies immediately.",
    "typical_cause": "Linux OOM killer when the machine runs out of memory; container cgroup memory limit exceeded (Docker or Kubernetes); or an explicit kill -9 from a supervisor or operator.",
    "remediation": "Reduce peak memory (stream/batch instead of loading all at once); raise the container memory limit or move to a larger machine; cap concurrency. Do not retry — the limit must be fixed first. Check kernel log: dmesg -T | grep -i 'out of memory'.",
    "source_url": "https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html",
    "verified_on": "2026-07-04"
  },
  {
    "id": "cron-exit-127",
    "exit_code_or_signal": "127",
    "name": "Command not found — PATH problem",
    "meaning": "Shell could not find the command to run. 127 is the shell's reserved code for command not found.",
    "typical_cause": "cron runs with a minimal PATH (often /usr/bin:/bin only); tools installed in /usr/local/bin or a version-manager shim directory are not on cron's PATH even though they are on the interactive shell's PATH.",
    "remediation": "Call the command by its absolute path (which aws), or set PATH explicitly at the top of the script: export PATH=\"/usr/local/bin:/usr/bin:/bin:$PATH\". Add set -euo pipefail so a 127 aborts loudly rather than continuing past the missing command.",
    "source_url": "https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html",
    "verified_on": "2026-07-04"
  },
  {
    "id": "cron-exit-143",
    "exit_code_or_signal": "143 (SIGTERM)",
    "name": "Terminated by SIGTERM — graceful shutdown",
    "meaning": "Process received SIGTERM (signal 15); 143 = 128 + 15. SIGTERM is catchable and allows the process to clean up before exiting.",
    "typical_cause": "Container or pod shutdown (Kubernetes/Docker sends SIGTERM before SIGKILL on a deploy or eviction); a job timeout that exceeded activeDeadlineSeconds; a systemctl stop or explicit kill (without -9) from an operator.",
    "remediation": "Trap SIGTERM in long-running batch scripts to save a checkpoint before exiting. Ping the monitor only after the job finishes its work; a SIGTERM mid-run withholds the ping and alerts on incomplete work versus routine deploys.",
    "source_url": "https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html",
    "verified_on": "2026-07-04"
  },
  {
    "id": "cron-exit-1",
    "exit_code_or_signal": "1",
    "name": "General error — cause is in the log",
    "meaning": "Catch-all failure status. The program ran and reported an error; the number itself carries no specific diagnostic information.",
    "typical_cause": "Python uncaught exception; a failing shell command under set -e; a linter or test runner that found problems; an expired credential or unreachable service — any program-reported failure that lacks its own reserved code.",
    "remediation": "Read the last lines of the job's output for the exception type, stack trace, or error message. Redirect stdout + stderr to a file (>> /var/log/job.log 2>&1) so cron does not discard the output. Reproduce by running the command outside cron to see stderr directly.",
    "source_url": "https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html",
    "verified_on": "2026-07-04"
  },
  {
    "id": "cron-silent-failure",
    "exit_code_or_signal": "N/A (no output)",
    "name": "Silent failure — job errors or stops without alerting",
    "meaning": "A job fails or stops running entirely and no alert fires. Not a specific exit code, but a class of failure: cron discards output by default and never sends an external notification on failure or on a run that never happens.",
    "typical_cause": "(1) cron discards output — error output goes to a local mailbox nobody reads. (2) cron never alerts on non-zero exit. (3) A removed crontab line or disabled schedule leaves no trace. (4) Scripts without set -e can exit 0 after a failed command.",
    "remediation": "Implement a dead man's switch: the job pings a URL only on success; an external monitor alerts if the ping doesn't arrive on schedule. This catches both silent classes — a job that errors and one that never runs. Use: set -euo pipefail && run_job && curl -fsS PING_URL.",
    "source_url": "https://man7.org/linux/man-pages/man5/crontab.5.html",
    "verified_on": "2026-07-04"
  },
  {
    "id": "cron-exit-2",
    "exit_code_or_signal": "2",
    "name": "Argument or usage error — invocation is wrong",
    "meaning": "Exit code 2 means the command was called with invalid arguments or a usage error. The program never did any real work; it exited immediately after failing to parse its arguments.",
    "typical_cause": "A flag was renamed or removed in a tool version upgrade; a required positional argument is missing; the binary on the cron server is a different version than the one tested interactively; a bad flag or option typo.",
    "remediation": "Run the command with the exact arguments cron uses, capturing stderr, and read the usage output for the bad flag name. Compare flags against --help. Pin CLI versions in the dependency manager. Check the absolute path of the binary cron resolves to.",
    "source_url": "https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html",
    "verified_on": "2026-07-05"
  },
  {
    "id": "cron-exit-126",
    "exit_code_or_signal": "126",
    "name": "Command found but not executable — permission denied",
    "meaning": "Shell found the file but could not execute it. Unlike exit 127 (file not found), the file exists but the execute bit is missing or the interpreter line is wrong.",
    "typical_cause": "Missing execute permission (chmod +x not set); the execute bit was present on the developer machine but git did not preserve it during deployment; a wrong or missing shebang line; a directory was passed where a script file was expected.",
    "remediation": "Add the execute bit: chmod +x /path/to/script. In git-managed repos set the bit in git: git update-index --chmod=+x script.sh. Verify the shebang line names a real interpreter that exists at that path on the cron server.",
    "source_url": "https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html",
    "verified_on": "2026-07-05"
  },
  {
    "id": "cron-exit-130",
    "exit_code_or_signal": "130 (SIGINT)",
    "name": "Interrupted by SIGINT — signal 2",
    "meaning": "Process received SIGINT (signal 2); 130 = 128 + 2. SIGINT is the same signal a terminal sends on Ctrl+C. It is catchable.",
    "typical_cause": "A CI runner (GitHub Actions, GitLab CI) cancelling a step; a supervisor or watchdog that sent kill -2; a timeout policy that uses SIGINT as its initial stop signal; a developer manually pressing Ctrl+C on a cron command being tested in a terminal.",
    "remediation": "Trap SIGINT in long-running batch scripts to checkpoint state before exiting. Ping the monitor only on a clean exit so an interrupted job withholds the ping and triggers an alert.",
    "source_url": "https://www.gnu.org/software/bash/manual/html_node/Exit-Status.html",
    "verified_on": "2026-07-05"
  },
  {
    "id": "cron-exit-139",
    "exit_code_or_signal": "139 (SIGSEGV)",
    "name": "Segmentation fault — invalid memory access",
    "meaning": "Process was killed by SIGSEGV (signal 11); 139 = 128 + 11. A segfault means the process accessed memory at an address it was not permitted to. The OS kills it immediately.",
    "typical_cause": "A bug in a C extension (Python/Ruby/PHP native extension such as numpy or a DB driver); a compiled binary dereferencing a null or out-of-bounds pointer; mismatched shared library (.so) versions from a partial upgrade; a deep recursive call overflowing the stack.",
    "remediation": "Enable faulthandler for Python (PYTHONFAULTHANDLER=1) to get a stack trace on crash. For compiled binaries, build a debug version and run under gdb or valgrind. A retry will not help — the code bug must be found and patched.",
    "source_url": "https://man7.org/linux/man-pages/man7/signal.7.html",
    "verified_on": "2026-07-05"
  },
  {
    "id": "cron-timeout",
    "exit_code_or_signal": "124 (timeout exit) / 137 (SIGKILL escalation)",
    "name": "Job timeout — run exceeded time limit",
    "meaning": "The coreutils timeout command exits 124 when the command exceeds the specified duration and is killed. If --kill-after is used and SIGTERM is ignored, the exit reflects the SIGKILL escalation (137).",
    "typical_cause": "A network call blocking indefinitely; a stuck lock or deadlock; I/O waiting on a slow disk or an unresponsive service; a job processing more data than expected in one run — any condition that causes the job to run past its configured time limit.",
    "remediation": "Wrap the cron job in: timeout --kill-after=30s 30m /path/to/job. Use flock -n <lockfile> to prevent overlapping runs when a job legitimately runs long. Set the monitor's window to normal run time plus a small margin so a hang surfaces before the next run overlaps.",
    "source_url": "https://www.gnu.org/software/coreutils/manual/html_node/timeout-invocation.html",
    "verified_on": "2026-07-04"
  }
]
