Error Handling and Exceptions¶
Overview¶
Handle failures explicitly with try/except/finally, raise meaningful errors, and map exceptions to clear stderr messages and exit codes for CI and cron.
Silent except Exception: pass turns outages into mysteries. Ops tools must fail loudly with context, clean up resources, and leave a useful exit code.
Complete File Handling first. Diagrams use Excalidraw only.
This is a core tutorial in Module 8 · Error Handling of the REBASH Academy Python for Cloud & DevOps Engineers series — written for Cloud, DevOps, Platform, and SRE engineers.
Prerequisites¶
Required¶
- File Handling — pathlib, JSON, YAML, CSV
- Project venv with Python 3.12+
Learning Objectives¶
By the end of this tutorial, you will be able to:
- Use
try/except/else/finallycorrectly - Catch specific exceptions (not bare
Exceptionby default) -
raiseand chain exceptions withfrom - Define a small custom exception type
- Apply defensive checks before risky I/O
- Map errors to non-zero exit codes
Architecture¶
This topic’s control points and relationships are shown below.
Theory¶
try / except / else / finally¶
try:
text = path.read_text(encoding="utf-8")
except FileNotFoundError:
print(f"error: missing {path}", file=sys.stderr)
return 1
else:
# runs only if no exception
process(text)
finally:
# always runs — close handles, temp cleanup hooks
...
Catch specifically¶
| Catch | When |
|---|---|
FileNotFoundError | Missing config |
ValueError / KeyError | Bad config shape |
json.JSONDecodeError | Corrupt JSON |
OSError | Broader I/O (use carefully) |
Exception | Last-resort boundary in main only |
Never swallow errors without logging.
raise and chaining¶
try:
port = int(raw)
except ValueError as exc:
raise ValueError(f"port must be int, got {raw!r}") from exc
Custom exceptions¶
Use for domain failures callers can handle differently from bugs.
Defensive programming¶
- Validate early (schema, required keys, ranges)
- Prefer fail-fast over partial mutation
- Default mutating tools to dry-run (Module 4)
- Timeouts on network I/O (Module 14)
Hands-on Lab¶
Focus: practise the core workflow for Error Handling and Exceptions
mkdir -p ~/rebash-python/module-08
cd ~/rebash-python/module-08
python3 -m venv .venv
source .venv/bin/activate
Step 1 – Config loader with specific catches¶
cd ~/rebash-python/module-08
source .venv/bin/activate
cat > load_json.py << 'EOF'
#!/usr/bin/env python3
from __future__ import annotations
import json
import sys
from pathlib import Path
class ConfigError(Exception):
pass
def load_replicas(path: Path) -> int:
try:
raw = path.read_text(encoding="utf-8")
except FileNotFoundError as exc:
raise ConfigError(f"missing config: {path}") from exc
try:
data = json.loads(raw)
except json.JSONDecodeError as exc:
raise ConfigError(f"invalid JSON in {path}: {exc}") from exc
if not isinstance(data, dict) or "replicas" not in data:
raise ConfigError("config must include integer 'replicas'")
try:
replicas = int(data["replicas"])
except (TypeError, ValueError) as exc:
raise ConfigError("replicas must be an integer") from exc
if replicas < 0:
raise ConfigError("replicas must be >= 0")
return replicas
def main(argv: list[str]) -> int:
if len(argv) != 2:
print("usage: load_json.py <file>", file=sys.stderr)
return 2
path = Path(argv[1])
try:
replicas = load_replicas(path)
except ConfigError as exc:
print(f"error: {exc}", file=sys.stderr)
return 1
print(replicas)
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv))
EOF
echo '{"replicas": 3}' > ok.json
echo '{"replicas": "x"}' > bad.json
python load_json.py ok.json
python load_json.py missing.json || true
python load_json.py bad.json || true
Step 2 – finally cleanup demo¶
python - <<'PY'
from pathlib import Path
scratch = Path("scratch.tmp")
try:
scratch.write_text("demo", encoding="utf-8")
raise RuntimeError("boom")
except RuntimeError as exc:
print("caught", exc)
finally:
if scratch.exists():
scratch.unlink()
print("cleaned scratch.tmp")
PY
Step 3 – Anti-pattern (do not copy)¶
python - <<'PY'
# Bad — hides the failure
try:
int("nope")
except Exception:
pass
print("looks fine but is not")
PY
Validation¶
- Lab commands run under
~/rebash-python/module-08/ - You can explain each Theory section in your own words
- You used modern tooling where it applies to this topic
- You can describe one production failure mode for this topic
Code Walkthrough¶
Production practice for Error Handling and Exceptions always combines:
- Inspect before you change (status, plan, logs, dry-run)
- Prefer reversible, documented changes (Git, IaC, drop-ins, version pins)
- Capture evidence (command output, pipeline logs) for handovers
- Prefer current tools and APIs over legacy shortcuts
- Least privilege — escalate credentials only when required
Keep runbooks short enough to follow under pressure. Automate checks; keep humans for judgement.
Security Considerations¶
- Treat credentials and tokens for python as privileged — never commit them
- Prefer short-lived auth (OIDC, roles, SSO) over long-lived keys
- Validate blast radius before apply/deploy/delete operations
- Restrict who can approve production changes
- Collect audit logs; limit who can read sensitive traces
Common Mistakes¶
Skipping fundamentals for Error Handling and Exceptions
Validate assumptions against the Theory section and official docs before changing production.
Treating lab defaults as production-ready for Error Handling and Exceptions
Lab shortcuts (open security groups, admin roles, skip approvals) must not ship unchanged.
Changing production without a rollback path
Always know how to revert (previous artefact, prior release, state rollback, DNS failback).
Best Practices¶
- Encode Error Handling and Exceptions changes as code and review them in pull requests
- Pin versions (images, modules, actions, provider plugins)
- Separate environments with clear promotion gates
- Alert on symptoms with runbooks attached
- Destroy lab resources; tag everything with owner and expiry where possible
Troubleshooting¶
| Symptom | Likely cause | What to do |
|---|---|---|
| Traceback in CI noise | Uncaught exception | Catch at main, log, exit |
| Empty catch | Swallowed error | Re-raise or return non-zero |
| Lost cause | raise New() without from | Chain with from exc |
Summary¶
- Catch specific errors; map to exit codes
- Custom exceptions for operator-facing config/domain failures
finallyfor cleanup; never silentpass- Fail fast with clear stderr
Interview Questions¶
- How does Error Handling and Exceptions show up when operating Cloud or production platforms?
- What would you check first if this area misbehaves in production?
- Which modern tools or APIs replace older equivalents here?
- What security control should accompany this capability?
- How would you automate verification of this topic in CI?
Sample answer — question 2
Start with blast radius and recent changes, gather evidence (logs, status, plan/diff), then fix forward with a known rollback path — not guesswork.