Troubleshooting
Goal of this page: get back to shipping when an update appears to be missing or a queue run seems to have stopped. Most of the time, the fix is simply using the command that belongs to the layer that needs changing.
There are two kinds of update
OpenSpec Shipper has two update steps, and they do different jobs:
npm updatechanges the version of the Shipper package installed in your repository.openspec-shipper updatecopies the templates and provider assets from that installed package into your project.
Running the second command does not download a newer npm package. It can only update from the version already present in node_modules.
Update to a newly published version
Start by asking the npm registry which version is public:
npm view openspec-shipper versionThen update the dependency and refresh the installed Shipper files:
npm update openspec-shipper --latest --force
npx openspec-shipper update --force
npx openspec-shipper doctorYou can confirm which version your repository is actually using:
npm list openspec-shipperIf npm still keeps an older version, install the version explicitly. This is useful when package-lock.json or the version range in package.json is holding the dependency back:
npm install -D openspec-shipper@1.0.6 --force
npx openspec-shipper update --forceReplace 1.0.6 with the version you want to use. To run a published version explicitly without relying on the local binary, use:
npx --yes openspec-shipper@1.0.6 update --forceWhen the npm cache is suspected
npm normally verifies its cache and downloads a package again when the cached content is invalid. Clearing the cache is therefore rarely necessary, but you can do it when you want to rule it out:
npm cache verify
npm cache clean --force
npm install -D openspec-shipper@1.0.6 --forceFor a completely clean test, use a temporary cache instead of touching your normal npm cache:
npm_config_cache="$(mktemp -d)" npm install -D openspec-shipper@1.0.6 --forceIf npm view openspec-shipper version still returns an older version, the problem is not your local cache: the registry has not received the version yet, or the package was published with a different dist-tag.
The queue looks stuck
An unchanged terminal line does not always mean that Shipper is blocked. An executor can spend several minutes thinking or running checks without writing a new line to the terminal. Start with the evidence that Shipper leaves for you:
npx openspec-shipper queue status
npx openspec-shipper queue dry-run
npx openspec-shipper doctorThen open .openspec-shipper/queue.md. Look at the task's phase, timestamps, status badge, and log link. The run log is usually more informative than the terminal because it contains the executor output and the last operation that was attempted.
To watch a log while it is being written:
tail -f .openspec-shipper/runs/<run-log>.logFor OpenCode, Shipper enables the executor's ERROR diagnostic stream by default and watches it live. A terminal quota, authentication, permission, model-availability, or provider error should therefore stop the current phase and appear in the run log within seconds, even when OpenCode itself would stay alive. Shipper does not send these failures to assisted recovery because a second model invocation cannot repair them. If an OpenCode run still shows only heartbeats after a provider error, check that OPENSPEC_SHIPPER_PRINT_LOGS has not been set to false and inspect the installed OpenCode version and its log format.
The queue's lock also tells you whether a runner is still alive:
cat .openspec-shipper/shipper.lock
ps -p <pid> -o pid=,etime=,command=Use the pid from the lock. Do not kill a random executor process just because it is using CPU; the lock identifies the queue runner that owns this project.
Stop a run safely
The normal stop is cooperative. It creates a stop request and lets the runner finish its current safe checkpoint:
npx openspec-shipper queue stopThis is the best choice when the current phase is close to completion. The command may return immediately while the queue continues finishing the current executor task.
If the runner is not progressing, the executor is unresponsive, or you need to regain control immediately, use the forced stop:
npx openspec-shipper queue stop --forceThe forced stop reads the active shipper.lock, verifies that its process is alive on this machine, and sends it SIGTERM. The runner interrupts the active child process, removes its lock, and exits. It does not silently delete your queue or reset your worktree.
Because the current phase may have been interrupted halfway through, inspect the project before restarting:
npx openspec-shipper doctor
npx openspec-shipper queue status
npx openspec-shipper queue dry-run
npx openspec-shipper queue runReconciliation uses the repository and GitHub evidence again, so you should not manually edit the phase just because the run was interrupted. If the task is genuinely blocked, Shipper marks it [!] and writes the reason and log link into queue.md.
The queue is blocked, but the terminal did not say so
Sometimes the last visible message is just waiting, or the terminal closes without printing a blocker. Treat the queue file as the starting point:
- Check whether the task has
[!],phase,reason, andlogmetadata. - Open the linked log and look at its final lines.
- Run
queue statusandqueue dry-runto force a fresh reconciliation. - If the underlying issue is fixed, change
[!]to[ ]and run the queue again.
That marker change is an explicit human retry. Shipper clears the current phase's execution and recovery counters before running it again, while keeping the history of other phases. This matters after changing provider credentials, models, permissions, or configuration: an old cycle-breaker budget must not immediately block the newly corrected attempt.
For a PR-related wait, use the PR link written in the queue and merge it on GitHub. Then run:
npx openspec-shipper queue runShipper will infer that the task can move forward. You do not need to guess whether it should resume at sync, archive, or cleanup.
A short recovery recipe
When you are unsure what happened, use this order:
npx openspec-shipper doctor
npx openspec-shipper queue status
npx openspec-shipper queue dry-run
npx openspec-shipper queue stopIf the process is still alive and not moving, replace the last command with:
npx openspec-shipper queue stop --forceAfter a forced stop, inspect the last log, fix the underlying issue, and run doctor, dry-run, and finally queue run again. If the problem remains unclear, share the reason from queue.md and the linked log with your AI assistant; those two artifacts usually contain enough context to diagnose it without starting over.