interpretExitCode function

String interpretExitCode(
  1. int exitCode,
  2. String command
)

Provide a semantic interpretation of an exit code for a given command.

Implementation

String interpretExitCode(int exitCode, String command) {
  if (exitCode == 0) return 'Success';

  final exe = extractExecutable(command) ?? '';

  // Command-specific overrides that conflict with signal range.
  // git uses 128 as a "fatal git error" sentinel even though 128 normally
  // means "killed by signal 0", so check it first.
  if (exitCode == 128 && (exe == 'git' || exe == 'gh')) {
    return 'Fatal git error';
  }

  // General signals.
  if (exitCode >= 128) {
    final signal = exitCode - 128;
    final signalNames = <int, String>{
      1: 'SIGHUP (hangup)',
      2: 'SIGINT (interrupt / Ctrl+C)',
      3: 'SIGQUIT (quit)',
      6: 'SIGABRT (abort)',
      9: 'SIGKILL (killed)',
      11: 'SIGSEGV (segmentation fault)',
      13: 'SIGPIPE (broken pipe)',
      14: 'SIGALRM (alarm)',
      15: 'SIGTERM (terminated)',
    };
    final name = signalNames[signal] ?? 'signal $signal';
    return 'Killed by $name (exit code $exitCode)';
  }

  // Command-specific codes.
  switch (exe) {
    case 'grep':
    case 'egrep':
    case 'fgrep':
    case 'rg':
    case 'ag':
      if (exitCode == 1) return 'No matches found';
      if (exitCode == 2) return 'Syntax error or inaccessible file';
      break;
    case 'diff':
      if (exitCode == 1) return 'Files differ';
      if (exitCode == 2) {
        return 'Trouble (missing file, permission denied, etc.)';
      }
      break;
    case 'test':
    case '[':
      if (exitCode == 1) return 'Condition evaluated to false';
      break;
    case 'curl':
      if (exitCode == 6) return 'Could not resolve host';
      if (exitCode == 7) return 'Failed to connect';
      if (exitCode == 22) return 'HTTP error (4xx or 5xx)';
      if (exitCode == 28) return 'Operation timed out';
      if (exitCode == 35) return 'SSL/TLS connection error';
      if (exitCode == 56) return 'Failure in receiving network data';
      break;
    case 'wget':
      if (exitCode == 1) return 'Generic error';
      if (exitCode == 4) return 'Network failure';
      if (exitCode == 8) return 'Server issued an error response';
      break;
    case 'ssh':
    case 'scp':
      if (exitCode == 255) return 'SSH connection failure';
      break;
    case 'git':
      if (exitCode == 1) {
        return 'Git operation failed (check output for details)';
      }
      if (exitCode == 128) return 'Fatal git error';
      break;
    case 'make':
      if (exitCode == 2) return 'Make encountered errors';
      break;
    case 'gcc':
    case 'g++':
    case 'clang':
    case 'clang++':
    case 'rustc':
    case 'javac':
    case 'tsc':
      if (exitCode == 1) return 'Compilation error';
      break;
    case 'python':
    case 'python3':
    case 'node':
    case 'ruby':
    case 'perl':
      if (exitCode == 1) return 'Runtime error or unhandled exception';
      if (exitCode == 2) return 'Misuse of command (invalid arguments)';
      break;
    case 'npm':
    case 'yarn':
    case 'pnpm':
      if (exitCode == 1) return 'Operation failed (check output)';
      break;
    case 'docker':
      if (exitCode == 1) return 'Docker command failed';
      if (exitCode == 125) return 'Docker daemon error';
      if (exitCode == 126) {
        return 'Command cannot be invoked (permission issue)';
      }
      if (exitCode == 127) return 'Command not found in container';
      break;
  }

  // Generic interpretations.
  switch (exitCode) {
    case 1:
      return 'General error';
    case 2:
      return 'Misuse of shell command (invalid arguments or syntax)';
    case 126:
      return 'Command found but not executable (permission denied)';
    case 127:
      return 'Command not found';
    default:
      return 'Non-zero exit code: $exitCode';
  }
}