Get Started
Drain & Graceful Exit
Taking a Lightchain AI worker offline is not the same as stopping its container. The worker has obligations on-chain — jobs it already completed carry a dispute window during which the disputer can still raise a challenge — and the on-chain registry refuses to release your staked LCAI while any of those obligations remain. Callingderegister before that settles reverts with ActiveJobsExist and leaves the stake locked.
This page is the operator runbook for shutting a worker down cleanly: stop it, wait out the dispute window, settle its jobs with release, then deregister. It applies to both testnet and mainnet — substitute the env values for your network.
Why the order matters
WorkerRegistry.deregisterWorker() reverts with ActiveJobsExist whenever getActiveJobCount(worker) > 0. After every completed job, the worker stays "active" for the on-chain dispute window (a parameter on AIConfig, which can be set per model). During that window the disputer can still raise a challenge, so the contract holds the slot open.
Once the dispute window passes, a call to JobRegistry.releaseJobs(...) settles the job: it decrements activeJobsCount and credits the fee to the worker's balance in JobRegistry. A running worker sends that call itself — its release scheduler probes every 5 minutes (RELEASE_PROBE_INTERVAL) and runs a full cycle within RELEASE_INTERVAL (default 8 h) of each job's expiry.
You can't simply keep the worker running until the count reaches 0. In dispatcher-free mode a running worker keeps claiming new sessions, and every new job starts a new dispute window. So the worker has to stop first, and the release is then run by hand from a one-off container, with the same image and keys.
Prerequisites
The examples below assume the setup from Run a Worker — Testnet or Mainnet: the env file~/lightchain-worker/worker.env and the keys directory ~/lightchain-worker/keys. Set these in your shell:
CodeBASH
lightchain-worker command in a one-off container with your env file and keys. It works while the worker container is stopped:
CodeBASH
cast (from Foundry) is used for read-only chain queries throughout.
Installed with the CLI (Run a Worker on Testnet with the CLI)? Use the lcw function from that guide, stop the worker with sudo systemctl stop lightchain-worker, and read the metrics with curl -s http://127.0.0.1:9101/metrics. The steps are otherwise the same.
Exit sequence
Stop, wait, release, optionally withdraw, deregister, remove. Steps 1–3 are mandatory; step 4 is optional housekeeping; steps 5–6 retire the worker.1. Stop the worker
A running worker keeps claiming sessions until the process stops.lightchain-worker drain doesn't change that: it sets a marker that only the retired dispatcher read, and nothing checks it before a claim.
Stop it:
CodeBASH
SHUTDOWN_TIMEOUT runs out (240 s in the install guides, 30 s when unset). A job still running then is cancelled: it times out on-chain and the timeout is recorded as an offence (see Slashing & Rehabilitation). Keep docker stop -t above SHUTDOWN_TIMEOUT, or Docker kills the worker before its jobs finish. Worker images before release v0.0.5 end a running job at SIGTERM; pull latest first.
Jobs submitted later in sessions the worker already claimed still target it. While it is offline they time out the same way.
Keep the container (no docker rm yet): step 2 reads its logs, and docker start lightchain-worker brings it back if you change your mind. Its release_state.json, the list of jobs waiting for release, is on the mounted volume (RELEASE_STATE_PATH=/data/release_state.json), and the one-off container in step 3 reads it from there.
2. Wait out the dispute window
A job can be released once the dispute window has passed since it completed. Read the window:CodeBASH
CodeBASH
3. Release
CodeBASH
release first brings the ledger up to date from the chain, then sends one releaseJobs transaction for the jobs that are past the window. It prints, for example:
CodeTEXT
pending after cycle: 0— everything is settled. Go on to the count check below.50 releasedwith jobs still pending — one run releases at most 50 jobs. Run it again.0 releasedwith jobs still pending — those jobs are still inside their dispute window, or under dispute. Run it again once the window has passed.no jobs ready for release— the ledger holds nothing to release.release cycle pausedorrelease cycle blocked: backoff active until chain time …— the contract was paused on the last attempt. Run it again after that time.
CodeBASH
release has nothing left to release, some jobs never completed. See the recovery scenarios.
4. (Optional) Withdraw earnings
Released fees accumulate as the worker's balance inJobRegistry. Check what's claimable, then transfer it to the worker address:
CodeBASH
5. Deregister
OnceactiveJobsCount is 0, deregister unlocks your staked LCAI and removes the worker from the registry:
CodeBASH
ActiveJobsExist, go back to step 3. A failed deregister doesn't change on-chain state; you can fix the condition and re-run.
6. Remove the container
Only afterderegister has succeeded:
CodeBASH
Quick reference
Recovery scenarios
"I randeregister and got ActiveJobsExist."
Your worker is still registered — the failed tx didn't burn anything. Run lcw release (after the dispute window), check getActiveJobCount, then retry deregister.
"activeJobsCount never drops."
release can only settle jobs that reached Completed. If your worker went offline part-way through a job, that job is frozen in Submitted or Acknowledged past its deadline, and no amount of waiting will clear it. See Stuck Jobs — clearing these carries a penalty, so check the cost before acting.
"I removed the container (docker rm) before deregistering."
Nothing is lost when RELEASE_STATE_PATH=/data/release_state.json is set, as in the current install guides: the ledger is on the mounted volume. Without it, the ledger was inside the container. Your claimable funds and registration still exist on-chain, and lcw release rebuilds the ledger from the chain before it releases. That scan covers the whole chain and takes a few minutes.
Important behaviors
- A running worker keeps claiming. It does not check the drain marker before a claim, so stopping the process is the only way to take it out of rotation.
- Stopping lets running jobs finish, up to a limit. SIGTERM stops new work at once and gives the jobs already running up to
SHUTDOWN_TIMEOUTto finish (worker release v0.0.5 on). Keepdocker stop -tabove it. - The release scheduler needs a running worker. A stopped worker settles nothing on its own;
lcw releasedoes it from a one-off container. deregisteris retryable. A failedderegister(e.g.ActiveJobsExist) doesn't change on-chain state. Fix the underlying condition and re-run; nothing is consumed by the revert.
Related pages
- Run a Worker — Testnet — the testnet install guide with the env file and keys directory used above.
- Run a Worker — Mainnet — the mainnet equivalent.
- Dispatcher-free Mode — how a self-claiming worker wins sessions, and what stopping it means for sessions it already claimed.
- Slashing & Rehabilitation — what happens when a worker misbehaves rather than retires; orthogonal to draining.
- Worker API — the indexer surface for observing
activeJobsCountand per-job lifecycle events.