name: nim-error-handling description: Design clear Nim error-handling flows. Use when reviewing failure behavior, exception boundaries, or batch processing.
Use this skill when deciding where code should raise, catch, translate, retry, or return structured failure data.
- Raise exceptions for all errors: invalid input, parsing failures, I/O failures, semantic violations.
- The Nim compiler and stdlib overwhelmingly use exceptions. This is not a close call.
- Use
newException(ValueError, msg)for bad input. UsenewException(IOError, msg)for I/O. UsenewException(OSError, msg)for OS errors. Pick the most specific stdlib type that fits.
- Catch specific exception types:
except ValueError,except IOError,except OSError. - Catch at module boundaries to translate (e.g.,
except OSError: raise newException(IOError, ...)). - Catch at orchestrator boundaries to aggregate (e.g., batch processor records per-item failures).
- Use
try/finallyfor cleanup, nottry/except.
- Do not catch
CatchableErrororExceptionin normal code.CatchableErrordoes not catchDefect, butExceptiondoes — and catching bugs is wrong. Both are too broad. Catch the specific types your code actually handles.
- Use
Option[T]whennoneis a valid outcome: search misses, optional struct fields, safe enum conversions. - Do not use
Option[T]to avoid raising on bad input. Bad input is an error. Raise.
- Add
{.raises: [X].}on exported procs with a stable exception surface. - Add
{.raises: [].}on procs guaranteed not to raise. - Do not annotate internal helpers by default.
- The compiler enforces raises contracts. Keep them accurate.
- Internal step procs raise exceptions. Do not return error codes, nil, or bool to signal failure.
- Boundaries catch, translate, or aggregate. Keep the success path straight-line between them.
- Do not wrap every call in try/except. Let exceptions propagate to the real boundary.
type
BatchItem = object
path: string
ok: bool
errorMsg: string
proc loadFile(path: string): string =
if path.len == 0:
raise newException(ValueError, "path is empty")
result = readFile(path)
proc processBatch(paths: seq[string]): seq[BatchItem] =
for path in paths:
try:
let content = loadFile(path)
result.add BatchItem(path: path, ok: true, errorMsg: "")
except ValueError, IOError:
result.add BatchItem(path: path, ok: false,
errorMsg: getCurrentExceptionMsg())| Mistake | Why it is wrong |
|---|---|
| Catching CatchableError or Exception | Too broad. Catch specific types. |
| Using Option[T] for bad input | Bad input is an error. Raise. |
| Returning nil or error codes from internal procs | Reimplements exception propagation. Just raise. |
| Catching in every layer | Let exceptions propagate to the real boundary. |
| Using try/except for cleanup | Use try/finally. |
- references/batch_preview_boundary.md
- references/retry_classification.md
- 2026-04-14: Rewritten from empirical compiler/stdlib review. Exceptions are default. Catch specific types at boundaries. Option[T] is for optional data only.
- 2026-04-11: Initial skill structure.
- 2026-04-09: Simplified rule set.