Skip to content

introduce specific exit codes for each fatal branch (#5696)#8212

Open
ChrisJr404 wants to merge 2 commits into
mitmproxy:mainfrom
ChrisJr404:specific-exit-codes
Open

introduce specific exit codes for each fatal branch (#5696)#8212
ChrisJr404 wants to merge 2 commits into
mitmproxy:mainfrom
ChrisJr404:specific-exit-codes

Conversation

@ChrisJr404

Copy link
Copy Markdown

Closes #5696. (Two prior attempts at this — #5133 and #5882 — were closed when their contributors went inactive. Picking it back up.)

Background

Every fatal path in mitmproxy / mitmdump / mitmweb funnels through sys.exit(1). A parent process (CI runner, supervisor, watcher script) only sees the integer code, so it can't distinguish

  • "you typed --invalid-opt" from
  • "the file we're saving flows to became unwritable mid-stream" from
  • "no TTY available, can't run the console UI"

…all three return 1. Real users have asked for this since #4669 in 2021.

Change

Add mitmproxy/utils/exit_codes.py with categorised constants. Categories are spaced by tens so future additions can slot into the appropriate group without renumbering existing ones (matches the design @Prinzhorn proposed in #5133):

GENERIC_ERROR        = 1     # backwards-compat catch-all
STARTUP_ERROR        = 10    # 10-19: startup-time problems
NO_UTF_CONSOLE       = 20    # 20-29: environment problems
NO_TTY               = 21
INVALID_ARGS         = 30    # 30-39: user-supplied input
INVALID_OPTIONS      = 31
CANNOT_PRINT         = 40    # 40-49: I/O problems
CANNOT_WRITE_TO_FILE = 41
DEBUG_EXIT           = 50    # 50-59: debugging hooks

Then thread the constants through every existing sys.exit(1) call site:

File Code
mitmproxy/addons/errorcheck.py STARTUP_ERROR
mitmproxy/addons/save.py CANNOT_WRITE_TO_FILE
mitmproxy/addons/termlog.py CANNOT_PRINT
mitmproxy/tools/console/master.py NO_TTY
mitmproxy/tools/main.py (argparse) INVALID_ARGS
mitmproxy/tools/main.py (OptionsError) INVALID_OPTIONS
mitmproxy/utils/debug.py (×2 MITMPROXY_DEBUG_EXIT) DEBUG_EXIT

The commented-out experimental # sys.exit(1) for the non-UTF-8 console branch in console/master.py:221 is left untouched — that's an active "let's see if not exiting affects users" experiment from 2022, not a fatal path that needs categorisation.

Tests

  • test/mitmproxy/utils/test_exit_codes.py (new) pins the codes for termlog (cannot print), save (cannot write), and errorcheck (startup error), plus a sanity check that all codes are distinct.
  • test/mitmproxy/addons/test_termlog.py::test_cannot_print already asserted on exit_code == 1; updated to use the new exit_codes.CANNOT_PRINT constant so the existing regression coverage now pins the right code.
$ pytest test/mitmproxy/addons/test_termlog.py test/mitmproxy/addons/test_errorcheck.py test/mitmproxy/addons/test_save.py test/mitmproxy/utils/ test/mitmproxy/tools/
229 passed, 1 skipped, 724 warnings in 3.47s

Notes

  • Diff is +185 / -9 across 10 files (one new module + one new test file + small edits at each call site).
  • No public-API change beyond the new exit_codes module — existing callers that read SystemExit.code from a subprocess just see a different integer for branches they previously couldn't disambiguate.
  • Numbering follows @Prinzhorn's original proposal exactly for backwards-compat with anyone who already wrote tooling against the prior PR's codes; DEBUG_EXIT = 50 is the only addition (the prior PRs didn't categorise the MITMPROXY_DEBUG_EXIT env-var path).
  • Happy to drop DEBUG_EXIT and revert utils/debug.py to sys.exit(1) if you'd prefer keeping the debug-only path uncategorised — let me know.

ChrisJr404 and others added 2 commits May 3, 2026 14:29
Every fatal path in mitmproxy / mitmdump / mitmweb funnelled through
sys.exit(1), which means a parent process (CI, supervisor, watcher
script) couldn't tell apart "you typed --invalid-opt" from "the file
we're saving flows to became unwritable mid-stream". Two prior PRs
attempted this fix (mitmproxy#5133 and mitmproxy#5882) but were both closed when the
contributors went inactive.

Add mitmproxy/utils/exit_codes.py with categorised constants:

  10-19  startup-time problems       (STARTUP_ERROR)
  20-29  environment problems        (NO_UTF_CONSOLE, NO_TTY)
  30-39  user-supplied input         (INVALID_ARGS, INVALID_OPTIONS)
  40-49  I/O problems                (CANNOT_PRINT, CANNOT_WRITE_TO_FILE)
  50-59  debugging hooks             (DEBUG_EXIT)

Categories are spaced by tens so future codes can slot into the
appropriate group without renumbering existing ones, mirroring the
design @Prinzhorn proposed in mitmproxy#5133.

Wire each existing sys.exit(1) call in:
  mitmproxy/addons/errorcheck.py    -> STARTUP_ERROR
  mitmproxy/addons/save.py          -> CANNOT_WRITE_TO_FILE
  mitmproxy/addons/termlog.py       -> CANNOT_PRINT
  mitmproxy/tools/console/master.py -> NO_TTY
  mitmproxy/tools/main.py           -> INVALID_ARGS, INVALID_OPTIONS
  mitmproxy/utils/debug.py          -> DEBUG_EXIT (x2)

Existing test_termlog.py::test_cannot_print updated to use the new
constant. New test/mitmproxy/utils/test_exit_codes.py pins the codes
for termlog (cannot print), save (cannot write), and errorcheck
(startup error), plus a sanity check that all codes are distinct.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add individual exit codes for different errors

1 participant