Error Handling¶
Understanding Kelora's error handling modes and how to diagnose issues.
Processing Modes¶
Kelora offers two error handling modes:
| Mode | Behavior | Use Case |
|---|---|---|
| Resilient (default) | Skip errors, continue processing | Production log analysis, exploratory work |
Strict (--strict) |
Fail-fast on errors | Data validation, CI/CD pipelines |
Resilient Mode¶
Overview¶
In resilient mode (default), Kelora continues processing even when errors occur:
- Parse errors: Skip unparseable lines, continue with next line
- Filter errors: Treat as
false, skip event - Transform errors: Return original event unchanged (atomic rollback)
- Summary: Show recovered runtime errors as warnings at end
- Exit code:
0while the run still did its job — even with skipped lines or rolled-back transforms. It becomes1only when a gate never once succeeded (a--filterthat errors on every event, or an input where no line parses), or on a structural /--assertfailure. Transform (--exec) errors are best-effort and never fail the run on their own. See Exit codes: the model.
When to Use¶
- Analyzing messy production logs
- Exploratory data analysis
- Real-time log streaming
- Mixed format log files
Example Behavior¶
Input:
Output:
Summary:
Error Recording¶
Errors are recorded but don't stop processing:
If e.timestamp is missing or invalid:
- Filter evaluates to
false - Event is skipped
- Error is recorded
- Processing continues
- Exit code stays
0as long as the filter succeeded on at least one event. If the filter errors on every event (e.g. a field-name typo likestatusinstead ofe.status), it never matched anything — that is a broken command, not data noise, so the run exits1. See Exit codes: the model.
Strict Mode¶
Overview¶
In strict mode (--strict), Kelora fails immediately on the first error:
- Parse errors: Show error, abort immediately
- Filter errors: Show error, abort immediately
- Transform errors: Show error, abort immediately
- Exit code: Non-zero on any error
When to Use¶
- Data validation pipelines
- CI/CD quality gates
- Critical processing where partial results aren't acceptable
- Debugging log parsing issues
Example Behavior¶
Input:
Output:
Exit code: 1
Processing stops at the first error. Line 3 is never processed.
Enabling Strict Mode¶
Error Types¶
Parse Errors¶
Occur when input lines can't be parsed in the specified format.
JSON parse error:
Input:
Resilient behavior:
- Line 1: Parsed successfully
- Line 2: Skipped (parse error recorded)
- Line 3: Parsed successfully
Strict behavior:
- Line 1: Parsed successfully
- Line 2: Error shown, processing aborts
- Line 3: Never processed
Filter Errors¶
Occur when --filter expressions fail during evaluation.
Example:
If e.timestamp is missing:
Resilient behavior:
- Filter evaluates to
false - Event is skipped
- Error recorded
Strict behavior:
- Error shown immediately
- Processing aborts
Transform Errors¶
Occur when --exec scripts fail during execution.
Example:
If e.value is not a valid integer:
Resilient behavior:
- Transformation rolled back (atomic)
- Original event returned unchanged
- Error recorded
- Exit code stays
0— exec is best-effort enrichment. Even an--execthat errors on every event is recovered (the original events still flow), so it never fails the run on its own. Use--strict(fail on the first error) or--assert(explicit gate) when a transform must succeed.
Strict behavior:
- Error shown immediately
- Processing aborts
Verbose Error Reporting¶
Default Error Reporting¶
By default, errors are collected and summarized at the end:
Summary:
Verbose Mode (--verbose)¶
Show each error immediately as it occurs:
Output:
⚠️ kelora: line 5: exec error - cannot convert 'abc' to integer
result=123
⚠️ kelora: line 12: exec error - cannot convert 'def' to integer
result=456
⚠️ kelora: line 23: exec error - field 'value' not found
result=789
Summary:
Multiple Verbosity Levels¶
-v # Show errors immediately
-vv # Show errors with more context
-vvv # Show errors with full details
Verbose with Strict¶
Combine for immediate errors and fail-fast:
Errors are shown immediately, then processing aborts.
What kelora prints¶
kelora follows the Unix rule of silence: a successful run prints only its
data. It speaks up only to flag a problem, point at a likely mistake, or report
an error — and -v/--verbose shows what it decided. Everything except the
event data goes to stderr, so a pipeline's stdout stays clean.
| Channel | Looks like | Appears when | Silence with |
|---|---|---|---|
| Events (data) | your records, on stdout | always — this is the result | -q / --quiet |
| Error ⚠️ | ⚠️ / kelora: |
the run hit a fatal problem | --silent (one fatal line still prints) |
| Warning 🔸 | kelora warning: |
something may be wrong but the run continued — recovered --exec errors, conflicting flags, mostly-failed parsing, lines truncated at --max-line-bytes |
--no-warnings, --no-diagnostics, --silent, KELORA_NO_WARNINGS |
| Hint 💡 | kelora hint: |
a likely mistake with a concrete fix — a quoted numeric filter that's always false, a format that fell back to whole-line | --no-hints, --no-diagnostics, --silent, KELORA_NO_HINTS |
| Status 🔹 | 🔹 / kelora: |
only under -v/--verbose — what kelora did: detected format, loaded config, applied defaults/aliases |
hidden by default |
Two consequences worth knowing:
- Success is silent. A confident auto-detection and a successfully-loaded
config are not announced on a normal run — their effects are the confirmation,
and a config that fails to load is a loud error. Run with
-vto see those decisions. Likewise, an empty result after your own filter prints nothing: a zero-match hint appears only when there is a concrete, non-obvious mistake to point at (e.g. a quoted numeric comparison, a typo'd field, no timestamps for--since/--until). - Warnings and hints are anomaly-triggered and reach redirected stderr (CI,
2>file), so a stuck user still sees them. Silence them with the flags above.
Quiet/Silent Controls¶
Each channel can be silenced independently; these flags compose for automation:
| Flag | Effect |
|---|---|
-q / --quiet |
Suppress events (formatter output) |
--no-diagnostics |
Suppress diagnostics/summaries (fatal line still emitted) |
--silent |
Suppress pipeline terminal output (events/diagnostics/stats/terminal metrics); script output allowed unless combined with --no-script-output or data-only modes; emit one fatal line on errors; metrics files still write |
--no-script-output |
Suppress Rhai print/eprint (implied by data-only modes) |
-s / --stats=FORMAT |
Show stats only (implies -q/--quiet; also suppresses script output; diagnostics stay on). Format: table, json |
-m / --metrics=FORMAT |
Show metrics only (implies -q/--quiet; suppresses diagnostics except fatal line, stats, script output). Format: short, full, tsv, json (bare -m auto-selects table on a terminal, tsv when piped) |
--with-stats |
Show stats alongside events (rare case) |
--with-metrics |
Show metrics alongside events (rare case) |
Examples:
kelora -q -j app.log --with-stats # No events; stats emit
kelora --silent -j app.log && echo "Clean" || echo "Has errors"
kelora -m --metrics-file metrics.json app.log # Metrics only
kelora --metrics=json app.log # Metrics in JSON format
Exit codes: the model¶
One rule captures Kelora's exit-code behavior:
Kelora exits non-zero when it couldn't do the job you asked — not because the data was messy.
That splits cleanly into three tiers:
| Tier | Exit | What it means | Examples |
|---|---|---|---|
| Recovered | 0 |
The run did its job. Individual records may have been skipped, rolled back, or left un-enriched, and they're reported as diagnostics. | A few unparseable lines among good ones; an --exec that errors on some (or even all) events |
| Couldn't do the job | 1 |
A gate never once succeeded, a forbidden operation, or a structural / explicit-gate failure. | A --filter that errors on every event; an input where no line parses; mutating conf outside --begin; a named input that can't be opened; an --assert violation |
| Invalid usage | 2 |
Bad command line — caught before processing. | Unknown flag, incompatible options, invalid config |
The key distinction is gates vs. transforms:
- Gates — parse and each
--filterstage — must work. If a gate never once succeeds, the output is empty or meaningless: no line parsed, or a filter that errored on every event it saw never actually selected anything (the dangerous case where "show me the errors" returns nothing and looks like success). That's a broken command, so it exits1. Each filter is gated individually, so a working first filter cannot mask a completely broken second one. A gate that errors on only some records is data noise and is recovered. - Transforms — exec — are best-effort. A failing
--execrolls back to the event as it was before that stage and emits it anyway, so the output stays valid even when the transform errors on every event. Exec errors are reported but never fail the run on their own. Use--strictto fail on the first error, or--assertfor an explicit data-quality gate.
This holds regardless of output flags — the signal is computed independently of
--stats/--no-diagnostics collection — so --metrics, --drain, -q, and
--no-diagnostics all preserve the exit code.
Scenario reference¶
| Scenario | Default | --strict |
|---|---|---|
| Clean run | 0 |
0 |
| Filter legitimately matches nothing | 0 |
0 |
| Some lines fail to parse, others succeed | 0 |
1 (aborts on first) |
| Every line fails to parse (wrong format) | 1 |
1 |
--filter errors on every event (typo, type bug) |
1 |
1 |
Broken --filter behind a working --filter (each filter is its own gate) |
1 |
1 |
--filter errors on some events, evaluates on others |
0 |
1 (aborts on first) |
--exec errors on some events (heterogeneous logs) |
0 |
1 (aborts on first) |
--exec errors on every event (best-effort transform) |
0 |
1 |
Broken --exec behind a selective --filter |
0 |
1 |
Mutating conf outside --begin (forbidden) |
1 |
1 |
| Named input file can't be opened | 1 |
1 |
--assert violation |
1 |
1 |
Full code table¶
| Code | Meaning |
|---|---|
0 |
The run did its job (possibly with recovered parse/exec errors) |
1 |
A gate (parse/filter) never succeeded, a structural failure, or an --assert/strict failure |
2 |
Invalid usage (CLI errors, incompatible flags) |
130 |
Interrupted (Ctrl+C) |
134 |
Internal thread panic (a bug — please report) |
141 |
Broken pipe (normal in Unix pipelines) |
143 |
Terminated (SIGTERM) |
Using Exit Codes¶
In shell scripts:
if kelora -q -j app.log; then
echo "✓ Ran successfully (messy records, if any, were recovered)"
else
echo "✗ Could not complete: a stage failed, or input was unusable"
exit 1
fi
Note that exit 0 means "the job got done", not "zero errors": a run with a
few recovered parse errors still succeeds. To fail on any imperfection, add
--strict; to fail on explicit data-quality rules, use --assert.
In CI/CD:
With automation:
Atomic Transformations¶
How It Works¶
In resilient mode, --exec scripts execute atomically:
If e.value.to_int() fails:
- Changes to
e.aare rolled back e.bis never sete.cis never set- Original event is returned unchanged
Why Atomic?¶
Prevents partial transformations from corrupting data:
Without atomicity:
// Input
{"value": "invalid"}
// Broken output (partial transformation)
{"value": "invalid", "a": 1} // Missing b and c!
With atomicity:
Multiple --exec Scripts¶
Each --exec script is atomic independently:
If first --exec fails:
- First transformation rolled back
- Second
--execstill runs on original event
If second --exec fails:
- First transformation preserved (it succeeded)
- Second transformation rolled back
Common Error Scenarios¶
Missing Fields¶
Problem:
Some events missing timestamp field.
Solution: Use safe access:
Type Mismatches¶
Problem:
e.value is a string, not a number.
Solution: Use type conversion with defaults:
Invalid Timestamps¶
Problem:
e.timestamp is not a valid timestamp.
Solution: Use safe access:
Array Index Out of Bounds¶
Problem:
e.items is empty or missing.
Solution: Check array length:
Division by Zero¶
Problem:
e.total is zero.
Solution: Add guard:
Debugging Strategies¶
Use Verbose Mode¶
See errors as they happen:
Enable Strict Mode¶
Find first error quickly:
Inspect Problematic Lines¶
Use --take to limit processing:
Process only first 100 lines to find issues faster.
Check Field Existence¶
Verify fields exist before accessing:
Use Type Checking¶
Verify field types before operations:
kelora -j --exec 'if type_of(e.value) != "i64" { eprint("Value is not integer: " + e.value) }' app.log
Validate Input Format¶
Test parsing with strict mode:
No output, but exits with error if parsing fails.
Error Messages¶
Parse Error Format¶
line 42: Line number in inputparse error: Error category- Details: Specific error message
Filter Error Format¶
Exec Error Format¶
Enhanced Error Summaries¶
With --verbose, get example errors:
🔹 Processed 1000 lines, 950 events output, 50 errors
Error examples:
line 42: exec error - cannot convert 'abc' to integer
line 103: exec error - field 'value' not found
line 287: filter error - timestamp is null
Best Practices¶
Use Resilient Mode for Production¶
Production logs are messy - resilient mode handles gracefully:
Use Strict Mode for Validation¶
Validate data quality in pipelines:
Combine Quiet and Exit Codes¶
For automation, use exit codes:
Add Defensive Checks¶
Use safe field access patterns:
Log Errors to File¶
Capture errors for later analysis:
Use Stats for Summary¶
Get error counts without verbose output:
Shows error count in summary.
Parallel Processing¶
Error Handling in Parallel Mode¶
When using --parallel, error handling works the same:
- Errors still recorded per event
- Summary shows total errors across all threads
- Exit code reflects any errors from any thread
Verbose with Parallel¶
Verbose errors are shown immediately, but may be interleaved:
Errors from different threads may appear out of order.
Strict with Parallel¶
First error from any thread aborts all processing:
See Also¶
- Pipeline Model - How error handling fits into processing stages
- Scripting Stages - Error handling in --begin/--exec/--end
- CLI Reference - All error handling flags
- Exit Codes Reference - Complete exit code documentation