Skip to content

[core]: error_handler.func

CanbiZ edited this page Dec 1, 2025 · 1 revision

Error-Handler.func Wiki

Comprehensive error handling and signal management module providing exit code explanations, error handlers with logging, and signal trap configuration for all Community-Scripts projects.


📋 Table of Contents


Overview

The error_handler.func module provides robust error handling infrastructure:

  • ✅ Comprehensive exit code mapping (1-255+ codes documented)
  • ✅ Detailed error messages with line numbers and commands
  • ✅ Signal trap configuration (ERR, EXIT, INT, TERM, RETURN)
  • ✅ Error logging to persistent files
  • ✅ Graceful cleanup on signal termination
  • ✅ Stack trace display for debugging
  • ✅ Integration with core.func message functions
  • ✅ Container-agnostic (works in Proxmox + LXC)

Error Handling Flow

Command Execution
      ↓
   ERROR (non-zero exit)
      ↓
  ERR Trap Triggered
      ↓
error_handler() called
      ↓
explain_exit_code() lookup
      ↓
Display error with line/command
      ↓
Check for log file
      ↓
Exit with original code

Exit Code Reference

Exit codes are categorized by source system. See api.func.md for comprehensive mapping documentation.

Quick Reference Table

Range Category Examples
0 Success (no error)
1-2 Shell errors Syntax error, operation not permitted
100-101 APT errors Package manager errors
126-139 System errors Command not found, segfault, OOM
200-231 Proxmox custom Container creation errors
210-234 Database errors PostgreSQL, MySQL connection issues
243-254 Runtime errors Node.js, Python, npm errors
255 DPKG fatal Package system fatal error

Error Handler Functions

explain_exit_code()

Purpose: Maps numeric exit codes to human-readable descriptions. Shared with api.func for consistency.

Signature:

explain_exit_code()

Parameters:

  • $1 - Exit code (0-255+)

Returns: Human-readable explanation string

Categories Handled:

  • Generic shell errors (1, 2, 126-128, 130, 137, 139, 143)
  • Package managers (100-101, 255)
  • Python (210-212)
  • Databases (PostgreSQL 231-234, MySQL 241-244, MongoDB 251-254)
  • Node.js/npm (243-249, 254)
  • Proxmox custom (200-231)
  • Default: "Unknown error"

Usage Examples:

# Example 1: Look up error code
explain_exit_code 127
# Output: "Command not found"

# Example 2: In error logging
error_desc=$(explain_exit_code "$exit_code")
echo "Error: $error_desc" >> /tmp/error.log

# Example 3: Unknown code
explain_exit_code 999
# Output: "Unknown error"

error_handler()

Purpose: Main error handler triggered by ERR trap. Displays detailed error information and exits.

Signature:

error_handler()

Parameters:

  • $1 (optional) - Exit code (default: current $?)
  • $2 (optional) - Command that failed (default: $BASH_COMMAND)
  • $3 (optional) - Line number (default: ${BASH_LINENO[0]})

Returns: Exits with original exit code (does not return)

Output Format:

[ERROR] in line 42: exit code 1 (General error): while executing command curl https://api.example.com

--- Last 10 lines of log file ---
[log content]
------------------------------------

Implementation Pattern:

error_handler() {
  local exit_code=${1:-$?}
  local command=${2:-${BASH_COMMAND:-unknown}}
  local line_number=${BASH_LINENO[0]:-unknown}

  # If successful, return silently
  if [[ "$exit_code" -eq 0 ]]; then
    return 0
  fi

  # Get human-readable error description
  local explanation=$(explain_exit_code "$exit_code")

  # Show cursor (might be hidden by spinner)
  printf "\e[?25h"

  # Display error using color messages
  if declare -f msg_error >/dev/null 2>&1; then
    msg_error "in line ${line_number}: exit code ${exit_code} (${explanation}): while executing command ${command}"
  else
    echo -e "\n${RD}[ERROR]${CL} in line ${line_number}: exit code ${exit_code}: ${command}\n"
  fi

  # Log error details if log file configured
  if [[ -n "${DEBUG_LOGFILE:-}" ]]; then
    {
      echo "------ ERROR ------"
      echo "Timestamp : $(date '+%Y-%m-%d %H:%M:%S')"
      echo "Exit Code : $exit_code ($explanation)"
      echo "Line      : $line_number"
      echo "Command   : $command"
      echo "-------------------"
    } >> "$DEBUG_LOGFILE"
  fi

  # Show last lines of log if available
  local active_log="$(get_active_logfile)"
  if [[ -s "$active_log" ]]; then
    local log_lines=$(wc -l < "$active_log")
    echo "--- Last 10 lines of log ---"
    tail -n 10 "$active_log"
    echo "----------------------------"
  fi

  exit "$exit_code"
}

Usage Examples:

# Example 1: Automatic trap (recommended)
trap 'error_handler $? "$BASH_COMMAND" $LINENO' ERR
# Error automatically caught and handled

# Example 2: Manual invocation (testing)
error_handler 1 "curl https://api.example.com" 42
# Output: Detailed error with line number

# Example 3: In conditional
if ! some_command; then
  error_handler $? "some_command" $LINENO
fi

Signal Traps

on_exit()

Purpose: Cleanup handler called on normal script exit or error.

Signature:

on_exit()

Parameters: None

Returns: Exits with captured exit code

Behavior:

  • Captures current exit code
  • Removes lock files if present
  • Exits with original exit code

Implementation Pattern:

on_exit() {
  local exit_code="$?"

  # Cleanup lock files
  [[ -n "${lockfile:-}" && -e "$lockfile" ]] && rm -f "$lockfile"

  # Preserve exit code
  exit "$exit_code"
}

Trap Configuration:

trap on_exit EXIT  # Always called on exit

on_interrupt()

Purpose: Handler for Ctrl+C (SIGINT) signal. Allows graceful shutdown.

Signature:

on_interrupt()

Parameters: None

Returns: Exits with code 130 (standard SIGINT exit code)

Output: Displays "Interrupted by user (SIGINT)" message

Implementation Pattern:

on_interrupt() {
  echo -e "\n${RD}Interrupted by user (SIGINT)${CL}"
  exit 130
}

Trap Configuration:

trap on_interrupt INT  # Called on Ctrl+C

Usage Example:

# Script interrupted by user:
# Ctrl+C pressed
# → on_interrupt() triggers
# → "Interrupted by user (SIGINT)" displayed
# → Exit with code 130

on_terminate()

Purpose: Handler for SIGTERM signal. Allows graceful shutdown on termination.

Signature:

on_terminate()

Parameters: None

Returns: Exits with code 143 (standard SIGTERM exit code)

Output: Displays "Terminated by signal (SIGTERM)" message

Implementation Pattern:

on_terminate() {
  echo -e "\n${RD}Terminated by signal (SIGTERM)${CL}"
  exit 143
}

Trap Configuration:

trap on_terminate TERM  # Called on SIGTERM

Usage Example:

# System sends SIGTERM:
# kill -TERM $PID executed
# → on_terminate() triggers
# → "Terminated by signal (SIGTERM)" displayed
# → Exit with code 143

Initialization & Setup

catch_errors()

Purpose: Sets up all error traps and signal handlers. Called once at script start.

Signature:

catch_errors()

Parameters: None

Returns: No explicit return value (configures traps)

Traps Configured:

  1. ERRerror_handler() - Catches command failures
  2. EXITon_exit() - Cleanup on any exit
  3. INTon_interrupt() - Handle Ctrl+C
  4. TERMon_terminate() - Handle SIGTERM
  5. RETURNerror_handler() - Catch function errors

Implementation Pattern:

catch_errors() {
  # Set strict mode
  set -Eeuo pipefail

  # Configure traps
  trap 'error_handler $? "$BASH_COMMAND" $LINENO' ERR
  trap on_exit EXIT
  trap on_interrupt INT
  trap on_terminate TERM
  trap 'error_handler $? "$BASH_COMMAND" $LINENO' RETURN
}

Usage Examples:

# Example 1: Alpine container script
#!/bin/sh
source <(curl -fsSL .../core.func)
source <(curl -fsSL .../error_handler.func)
load_functions
catch_errors

# Now all signals handled automatically
update_os

# Example 2: Proxmox host script
#!/bin/bash
source <(curl -fsSL .../core.func)
source <(curl -fsSL .../error_handler.func)
load_functions
catch_errors

# Safe to proceed with error handling
create_container

Error Logging

Log File Configuration

Active Log Detection:

# In build.func/install.func:
BUILD_LOG="/tmp/create-lxc-${SESSION_ID}.log"
INSTALL_LOG="/root/install-${SESSION_ID}.log"
SILENT_LOGFILE="$(get_active_logfile)"  # Points to appropriate log

Log Output Behavior

When command fails in silent():

  1. Last 10 lines of log file are displayed
  2. Full log path shown if more than 10 lines
  3. Error message includes line number where failure occurred
  4. Command that failed is displayed

Accessing Error Logs

From Proxmox host:

# Host-side container creation log
/tmp/create-lxc-<SESSION_ID>.log

# View error details
tail -50 /tmp/create-lxc-550e8400.log
grep ERROR /tmp/create-lxc-*.log

# Development mode persistent logs
/var/log/community-scripts/create-lxc-<SESSION_ID>-<TIMESTAMP>.log

From inside LXC container:

# Container installation log
/root/install-<SESSION_ID>.log

# View recent errors
tail -20 /root/install-550e8400.log

Best Practices

1. Always Setup Traps Early

#!/bin/bash
set -Eeuo pipefail

source <(curl -fsSL .../core.func)
source <(curl -fsSL .../error_handler.func)
load_functions
catch_errors  # MUST be called before any real work

# Now safe - all signals handled

2. Use Meaningful Error Exit Codes

# Use Proxmox custom codes for container-specific errors
if [[ "$CTID" -lt 100 ]]; then
  msg_error "Container ID must be >= 100"
  exit 205  # Proxmox custom code
fi

# Use standard codes for common errors
if ! command -v curl &>/dev/null; then
  msg_error "curl not installed"
  exit 127  # Command not found
fi

3. Log Context Information

# In error_handler, DEBUG_LOGFILE receives:
DEBUG_LOGFILE="/tmp/debug.log"

# All errors logged with timestamp and details
{
  echo "Error at $(date)"
  echo "Exit code: $exit_code"
  echo "Command: $command"
} >> "$DEBUG_LOGFILE"

4. Graceful Signal Handling

# Setup signal handlers for cleanup
cleanup() {
  [[ -f "$temp_file" ]] && rm -f "$temp_file"
  [[ -d "$temp_dir" ]] && rm -rf "$temp_dir"
}
trap cleanup EXIT

# Now temporary files always cleaned up

5. Test Error Paths

# Force error for testing
false  # Triggers error_handler
# or
exit 1  # Custom error

# Verify error handling works correctly
# Check log files and messages

Debugging

Enable Stack Trace

# Via environment variable
DEV_MODE_TRACE=true bash script.sh

# Or in script
set -x  # Show all commands
trap 'error_handler $? "$BASH_COMMAND" $LINENO' ERR

View Full Error Context

# Show full log file instead of last 10 lines
DEBUG_LOGFILE="/tmp/full-debug.log"

# After error, review complete context
less /tmp/full-debug.log

Test Error Handler

# Manually trigger error handler
bash -c 'source <(curl -fsSL .../error_handler.func); catch_errors; exit 42'

# Should display:
# [ERROR] in line N: exit code 42 (Unknown error): ...

Contributing

Adding New Error Codes

  1. Assign code in appropriate range (see Exit Code Reference)
  2. Add description to explain_exit_code() in both:
    • error_handler.func
    • api.func (for consistency)
  3. Document in exit code table
  4. Update error mapping documentation

Improving Error Messages

Example: Make error message more helpful:

# Before:
"Container ID must be >= 100"

# After:
"Invalid CTID: $CTID. Container IDs must be >= 100. Current range: 100-999"

Testing Signal Handlers

# Test INT signal (Ctrl+C)
bash -c 'source <(curl -fsSL .../error_handler.func); catch_errors; sleep 30' &
PID=$!
sleep 1
kill -INT $PID
wait

# Test TERM signal
bash -c 'source <(curl -fsSL .../error_handler.func); catch_errors; sleep 30' &
PID=$!
sleep 1
kill -TERM $PID
wait

Notes

  • Error handler is required for all scripts (ensures safe cleanup)
  • Exit codes are standardized (0 = success, 1-255 = specific errors)
  • Signals are trapped to allow graceful shutdown
  • Lock files are automatically cleaned on exit
  • Log files contain full error context for debugging

Clone this wiki locally