- Posted on
- • Artificial Intelligence
Generating Error Handling Logic Using Artificial Intelligence
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Generating Error Handling Logic Using Artificial Intelligence (for Bash)
When bash scripts fail, they often fail silently. One missing directory, one unchecked return code, and your “quick script” becomes an overnight fire drill. What if you could use AI to scaffold strong, consistent error handling—fast—then enforce it with static analysis and lightweight tests?
This post shows how to combine AI-assisted code generation with proven Bash safety patterns so your scripts fail loudly, log clearly, and recover gracefully. You’ll get:
Why AI is a good fit for error-handling scaffolds
A minimal, reusable Bash error-handling module
A safe workflow to generate, validate, and integrate AI-suggested logic
Real-world examples (retry logic, cleanup traps, and more)
Package installation commands for apt, dnf, and zypper
Why AI for Bash error handling?
Bash error handling is 80% patterns and boilerplate. LLMs are good at pattern-heavy scaffolding (traps, retries, logging wrappers).
Humans are still essential for intent and context; pair AI with guardrails like
shellcheck,bash -n, and smoke tests to keep quality high.Consistency scales. AI helps you standardize how your team handles errors across many scripts (same log shape, same trap discipline, same retry).
Prerequisites and installation
We’ll use:
curl: talk to an API
jq: extract AI output from JSON
shellcheck: lint and catch pitfalls
bats (optional): quick test harness
Install with your package manager:
Debian/Ubuntu (apt)
sudo apt update sudo apt install -y curl jq shellcheck batsFedora/RHEL/CentOS Stream (dnf)
sudo dnf install -y curl jq ShellCheck batsopenSUSE (zypper)
sudo zypper refresh sudo zypper install -y curl jq ShellCheck bats
Note: Package name for ShellCheck is lowercase on apt and capitalized on dnf/zypper.
Core content: 5 steps to AI-powered, robust Bash
1) Start with a reusable error-handling module
Create errors.sh you can source from any script. This module:
Enforces strict modes
Installs a detailed
ERRtrapProvides logging, die, retry, and required-commands checks
Handles cleanup on
EXIT,INT,TERM
# errors.sh
# Reusable error handling utilities for Bash scripts.
# Source this file near the top of your script.
# shellcheck shell=bash
if [[ -n "${__ERRORS_SH:-}" ]]; then
return 0
fi
readonly __ERRORS_SH=1
# Strict modes
set -Eeuo pipefail
shopt -s inherit_errexit || true # Bash >=4.4, ignore if not available
IFS=$'\n\t'
# Basic logging (levels: INFO, WARN, ERROR)
log() {
local level="$1"; shift
local ts
ts="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
printf '%s [%s] %s\n' "$ts" "$level" "$*" >&2
}
die() {
local code="${1:-1}"; shift || true
log ERROR "${*:-Unspecified error}"
exit "$code"
}
# Report detailed error context
_err_report() {
local exit_code="$1"
# Use arrays for call stack info
local src="${BASH_SOURCE[1]:-?}"
local line="${BASH_LINENO[0]:-?}"
local func="${FUNCNAME[2]:-main}"
log ERROR "Command: ${BASH_COMMAND}"
log ERROR "Location: ${src}:${line} in ${func}()"
log ERROR "Exit code: ${exit_code}"
}
# Trap errors globally (inherited to functions via -E)
trap '_err_report "$?"' ERR
# Cleanup trap (override in your script if needed)
_cleanup() {
# Put idempotent cleanup here; scripts may override.
:
}
trap '_cleanup' EXIT INT TERM
# Require necessary commands before proceeding
require_cmds() {
local missing=0
for c in "$@"; do
command -v "$c" >/dev/null 2>&1 || { log ERROR "Missing required command: $c"; missing=1; }
done
(( missing == 0 )) || die 127 "Install missing dependencies and retry."
}
# Simple retry with exponential backoff
with_retry() {
local attempts="${1:-3}"
local base_delay="${2:-1}"
shift 2
local n=1
until "$@"; do
local rc=$?
if (( n >= attempts )); then
log ERROR "Retry failed after ${attempts} attempts (last rc=$rc) for: $*"
return "$rc"
fi
local delay=$(( base_delay * 2 ** (n-1) ))
log WARN "Attempt $n failed (rc=$rc). Retrying in ${delay}s: $*"
sleep "$delay"
((n++))
done
}
# Safer mktemp directory helper
mktempdir() {
mktemp -d 2>/dev/null || die 1 "mktemp failed"
}
Use it like this in your script:
#!/usr/bin/env bash
# example.sh
# Load error module early
source "./errors.sh"
require_cmds curl jq
main() {
log INFO "Starting job"
with_retry 5 1 curl -fsS https://example.com/api | jq -r '.status' >/dev/null
log INFO "Job finished OK"
}
# Only run main if executed directly, not when sourced
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
main "$@"
fi
Why it works:
set -Eeuo pipefail: aborts on error, undefined variables, and failed pipelines
ERR trap with call-site info: you see the failing command, file, and line
Retry wrapper prevents transient network blips from crashing your script
require_cmds fails fast with actionable messages
2) Use AI to generate context-specific guards and wrappers
Let AI draft boilerplate guards tailored to your task (e.g., argument validation, preflight checks, checksum verification). Keep the prompt focused and ask for POSIX/Bash-4 compatible constructs.
Example prompt template:
You are generating robust Bash error-handling helpers to drop into a script that:
- Downloads an artifact from a URL
- Verifies its SHA256 checksum
- Extracts it to a target directory
Requirements:
- Bash (not zsh), strict modes assumed
- Use functions: validate_args, check_writable_dir, download_artifact, verify_sha256
- No external deps beyond curl, sha256sum, tar
- Return non-zero on failures; do not exit in helpers (let caller decide)
- Log to stderr with clear, single-line messages
- Avoid command substitutions that mask exit codes in pipelines
Output only Bash code, no commentary.
Calling an OpenAI-compatible endpoint with curl and jq (generic; works with many hosted or self-hosted APIs):
# Environment:
# LLM_API_URL: e.g., https://api.openai.com
# LLM_API_KEY: your token
# MODEL: e.g., gpt-4o-mini
curl -sS "${LLM_API_URL}/v1/chat/completions" \
-H "Authorization: Bearer ${LLM_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"model": "'"${MODEL}"'",
"messages": [
{"role": "system", "content": "You write secure, portable Bash."},
{"role": "user", "content": "'"$(printf '%s' "$(cat prompt.txt)" | sed 's/"/\\"/g')"'"}
],
"temperature": 0.2
}' \
| jq -r '.choices[0].message.content' > generated_helpers.sh
Now you have generated_helpers.sh you can source after errors.sh.
3) Validate generated Bash before you trust it
Never paste AI output straight into production. Validate it like any other code:
Syntax check:
bash -n generated_helpers.shShellCheck with errors only:
shellcheck -S error generated_helpers.shQuick smoke test with bats (optional): Create
test_generated.bats:#!/usr/bin/env bats setup() { source "./errors.sh" source "./generated_helpers.sh" } @test "validate_args fails on missing URL" { run validate_args "" "/tmp" "abc123" [ "$status" -ne 0 ] }Run:
bats test_generated.bats
CI tip: Fail the pipeline if shellcheck or bash -n fail. Keep temperature low in the generation step for deterministic output.
4) Compose patterns into a real script
Here’s a compact example that downloads, verifies, and extracts an archive with retries and cleanup. It combines errors.sh + AI-generated helpers (or your own):
#!/usr/bin/env bash
# fetch_and_extract.sh
source "./errors.sh"
source "./generated_helpers.sh" # assumes it defines: validate_args, download_artifact, verify_sha256, extract_to
ARTIFACT_URL="${1:-}"
TARGET_DIR="${2:-/opt/myapp}"
EXPECTED_SHA256="${3:-}"
_cleanup() {
# Remove temp dir if it was created
if [[ -n "${TMPDIR_FAE:-}" && -d "${TMPDIR_FAE}" ]]; then
rm -rf -- "${TMPDIR_FAE}"
fi
}
export -f _cleanup # ensure trap sees it
main() {
require_cmds curl sha256sum tar
validate_args "${ARTIFACT_URL}" "${TARGET_DIR}" "${EXPECTED_SHA256}" \
|| die 2 "Invalid arguments. Usage: $0 <url> <target_dir> <sha256>"
check_writable_dir "${TARGET_DIR}" || die 3 "Target not writable: ${TARGET_DIR}"
TMPDIR_FAE="$(mktempdir)"
local outfile="${TMPDIR_FAE}/artifact.tar.gz"
log INFO "Downloading: ${ARTIFACT_URL}"
with_retry 4 2 download_artifact "${ARTIFACT_URL}" "${outfile}" \
|| die 10 "Download failed"
verify_sha256 "${outfile}" "${EXPECTED_SHA256}" \
|| die 11 "Checksum mismatch"
extract_to "${outfile}" "${TARGET_DIR}" \
|| die 12 "Extraction failed"
log INFO "Done -> ${TARGET_DIR}"
}
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
main "$@"
fi
If the AI didn’t produce check_writable_dir, write it or prompt for it explicitly. Keep helpers small and composable.
5) Close the loop: let runtime errors improve your script
Use real failures to drive future improvements. Capture logs, redact secrets, then ask the AI for hardening suggestions.
Example sanitized log snippet:
2026-07-06T12:01:09Z [INFO] Downloading: https://cdn.example.com/app.tar.gz 2026-07-06T12:01:12Z [WARN] Attempt 1 failed (rc=22). Retrying in 1s: curl -fsS ... 2026-07-06T12:01:14Z [WARN] Attempt 2 failed (rc=22). Retrying in 2s: curl -fsS ... 2026-07-06T12:01:21Z [ERROR] Retry failed after 4 attempts (last rc=22) for: curl -fsS ... 2026-07-06T12:01:21Z [ERROR] Command: curl -fsS ... 2026-07-06T12:01:21Z [ERROR] Location: fetch_and_extract.sh:41 in main() 2026-07-06T12:01:21Z [ERROR] Exit code: 22Improvement prompt idea:
The download intermittently fails with rc=22. Propose a curl-based download helper that: - Adds random jitter to backoff - Switches to a mirror on HTTP 404/5xx - Validates expected content-type - Exposes configurable timeouts via env vars Output Bash only; keep dependencies to curl and awk.
Validate, integrate, repeat.
Real-world snippet: safer curl download with jitter and content-type check
Ask your AI for something like this, then lint and test it:
# Requires: curl, awk
download_artifact() {
local url="$1" out="$2"
: "${CURL_TIMEOUT:=15}"
: "${CURL_CONNECT_TIMEOUT:=5}"
: "${ACCEPT_CT:=application/gzip}"
# Write to temp then move to avoid partials
local tmp="${out}.part"
rm -f -- "$tmp"
# -f: fail on HTTP errors; -S: show errors; -s: silent progress
curl -fSsv \
--connect-timeout "$CURL_CONNECT_TIMEOUT" \
--max-time "$CURL_TIMEOUT" \
-H "Accept: ${ACCEPT_CT}" \
-o "$tmp" "$url" 2> >(awk '{print strftime("%Y-%m-%dT%H:%M:%SZ"), "[CURL]", $0 >> "/dev/stderr"}') \
|| return $?
# Basic content-type check via HEAD request
local ct
ct="$(curl -fsSI -H "Accept: ${ACCEPT_CT}" "$url" | awk -F': ' 'tolower($1)=="content-type"{print tolower($2)}' | tr -d '\r')"
[[ "$ct" == *"${ACCEPT_CT}"* ]] || { log ERROR "Unexpected content-type: ${ct:-unknown}"; return 65; }
mv -f -- "$tmp" "$out"
}
# Jittered retry wrapper example:
with_retry_jitter() {
local attempts="${1:-5}" base="${2:-1}"; shift 2
local n=1
until "$@"; do
local rc=$?
if (( n >= attempts )); then
log ERROR "Exhausted ${attempts} attempts (rc=$rc) for: $*"
return "$rc"
fi
# jitter in [0, base]
local jitter
jitter="$(awk -v b="$base" 'BEGIN{srand(); printf "%.3f", rand()*b}')"
local delay
delay=$(awk -v n="$n" -v b="$base" -v j="$jitter" 'BEGIN{printf "%.3f", (b * (2^(n-1))) + j}')
log WARN "Attempt $n failed (rc=$rc). Retrying in ${delay}s: $*"
sleep "$delay"
((n++))
done
}
Swap this into your script’s download path and observe clearer diagnostics and more resilient transfers.
Common pitfalls AI can help you avoid
Forgetting
set -o pipefail: pipelines masking failuresWriting helpers that
exitinstead of returning non-zero: hard to compose, breaks librariesTraps that don’t include location/command: you get “something failed” instead of “what, where, and why”
Unchecked external dependencies: scripts run fine on your box, fail elsewhere
Partial writes: not using
*.partthenmvfor atomic-ish file updates
Prompt your AI to target these pitfalls explicitly in its output.
Conclusion and next steps
You don’t need to handcraft every trap, retry, and log line. Let AI generate the error-handling scaffolds, then put them through a simple pipeline: bash -n → shellcheck → bats. Combine the generated helpers with a small, battle-tested module like errors.sh, and you’ll get Bash that fails fast, explains itself, and recovers when it can.
Call to action:
Add
errors.shto your repo and standardize on its patternsWire a “generate → validate → integrate” job into CI
Start with one high-impact script this week (backups, deploys, data sync), and harden it using the flow above
If you want a follow-up post with a ready-made Makefile and CI snippets for this pipeline (including apt/dnf/zypper setup steps), say the word.