Skip to main content
Checkpoints are the mechanism that makes cl-pipelines crash-safe. Every checkpoint is a small serialized record stored by your StorageAdapter. When a phase completes or explicitly saves progress, the library writes a new checkpoint. When the pipeline finishes, it clears it. At no point is in-progress state held only in memory — if your process dies, the data survives in storage.

Checkpoint shape

The nextPhase field is the lookup key advancePhase uses to find the right Phase object. If you rename a phase in your code after a checkpoint has been written, the resume will fail with a “phase not found” error — handle renames carefully in production.

When checkpoints are written

When ctx.saveState(state) is called, nextPhase is set to the current phase name — not the next one. This ensures that a crash re-enters the same phase, not skips ahead to the next one.

When checkpoints are cleared

The checkpoint is set to null and status is set to "complete" only when a phase returns { kind: "done" }. In all error cases — thrown exceptions or { kind: "error" } returns — the checkpoint is preserved so you can resume later.

Resume semantics

When you call runPipeline on a job that already has a checkpoint, retryMode determines what happens:
resolveStartPhase returns checkpoint.nextPhase. The existing checkpoint state is loaded and passed to the phase as ctx.checkpoint.state. Only the phase that was running (or waiting to run) at the time of the crash re-executes.
This is the right default. Use it for your “Retry” button.

Resume example: wiring a retry button

The following shows how you might hook runPipeline to a “Retry” button in your application. Because runPipeline only writes to storage and enqueues a scheduler event, it’s safe to call directly from a UI action or mutation handler:
You don’t need to check whether a checkpoint exists before calling runPipeline with retryMode: "resume". If there’s no checkpoint, the library falls back to starting fresh — the option is safe to use unconditionally as your retry default.

Inspecting checkpoints in tests

The in-memory storage adapter’s _inspect() method lets you examine checkpoint state directly in tests:
storage._inspect() is a test-only helper. Never read checkpoint state directly in production code — use StorageAdapter.getJob() instead.