Recovery and Resume
Rivet stores export progress in a SQLite state file (.rivet_state.db) located
next to the config file. This guide covers how to inspect, resume, and reset
export state correctly.
State file location
The state file is always created next to the config file:
./rivet.yaml ← config
./.rivet_state.db ← state (created automatically on first run)
To use a different location, point --config at the desired directory.
Export modes and state
| Mode | What is stored | Resume behaviour |
|---|---|---|
full | Completed file list (manifest) | No resume needed — re-run starts a fresh export |
incremental | Last cursor value | Re-run starts from where it left off |
chunked | Per-chunk completion status | --resume continues from the last completed chunk |
time_window | Nothing — no cursor is stored | Each re-run re-evaluates the rolling window from NOW(); windows overlap by design |
cdc | Log position (PostgreSQL slot / MySQL binlog checkpoint / SQL Server LSN / MongoDB resume token) | Resumes streaming from the last committed change position |
--resume for chunked exports
--resume is only meaningful for chunked mode with chunk_checkpoint: true
(not the default — set it so progress is recorded per chunk). It requires an
in-progress (not yet completed) checkpoint run in the state file. On a
full/incremental export --resume has no effect and warns.
Resume an interrupted export
# Start the export
rivet run --config rivet.yaml --export big_table
# If it was interrupted, resume it
rivet run --config rivet.yaml --export big_table --resume
What happens if no checkpoint exists
If --resume is called without a prior in-progress run, Rivet exits non-zero
with a clear message:
error: --resume requires an in-progress chunked export in state;
run without --resume to start a fresh export.
Do not use --resume to start a fresh export. It is only for continuing
interrupted runs.
What happens after a completed export
After a chunked export completes normally, --resume also exits non-zero:
error: --resume found a completed export (not in-progress);
use `rivet run` (without --resume) to start a new run.
This prevents accidentally treating a completed export as resumable.
--resume on full or incremental mode
--resume is silently validated for full/incremental exports — a plan
validation warning is emitted:
[resume-no-checkpoint] export 'X': --resume has no effect on full/incremental
exports. Remove --resume to suppress this warning.
The export proceeds normally. The flag is ignored.
Inspecting state
rivet state show --config rivet.yaml
This shows the current cursor value for each incremental export. For chunk
completion status use rivet state chunks --config rivet.yaml --export big_table,
and for per-run history (rows, bytes, duration, peak RSS) use
rivet metrics --config rivet.yaml.
Resetting state
Reset cursor for incremental exports
rivet state reset --config rivet.yaml --export incremental_export
The next run will re-export all rows from the beginning.
Reset chunk state for chunked exports
rivet state reset-chunks --config rivet.yaml --export big_table
After reset, the next rivet run (without --resume) starts fresh from
chunk 0.
Important: After reset-chunks, do not use --resume — there is no
checkpoint to resume from.
Common operator mistakes
Mistake 1: Using --resume after reset
rivet state reset-chunks --config rivet.yaml --export big_table
rivet run --config rivet.yaml --export big_table --resume # WRONG
Fix: omit --resume after a reset.
rivet run --config rivet.yaml --export big_table # correct
Mistake 2: Using --resume to start a fresh chunked export
# First run ever — no state exists
rivet run --config rivet.yaml --export big_table --resume # WRONG
Fix: do not use --resume on the first run.
Mistake 3: Pointing to a different config file for resume
The state file is tied to its config directory. If you copy the config to a new location, the state file is not copied with it — the resumed export starts fresh.
Crash recovery
If the process is killed mid-export:
-
Incremental — the cursor is committed once per run, after the run’s manifest is durable. A crash mid-export leaves the cursor at the previous run’s value, so re-running re-exports the whole window — into new, uniquely-timestamped part files (names embed a per-run millisecond stamp). Files the crashed run already committed are complete, never partial, and are not overwritten — they remain in the prefix as at-least-once duplicates, which downstream consumers must tolerate (load from the manifest, see semantics.md).
-
Chunked — each chunk is committed to state only after it writes successfully. A crash mid-chunk means that chunk is retried on
--resume. Completed chunks are not re-exported. This holds for both the sequential checkpoint loop (parallel: 1) and the parallel worker pool (parallel: Nwithchunk_checkpoint: true); when one parallel worker panics,reset_stale_running_chunk_tasksresets everyrunningtask back topendingon resume so no work is lost. Coverage:live_chunked_recoveryC1–C4 (see reliability-matrix.md § Failure-mode coverage). -
Full — full exports have no cursor. Re-running after a crash starts from the beginning and writes new, uniquely-timestamped part files; anything the crashed run left behind stays in the prefix as an orphan (no manifest names it) — load from the manifest, or clean orphans with
gc_orphans.