### Bug type
Behavior bug (incorrect output/state without crash)
### Beta release blocker
No
### Summary
After `openclaw plugins update` installs external npm plugins into new `__openclaw-generation__g-*` project directories, the next Gateway start logs `cleaned N retained npm plugin generation(s)` and deletes **the generations that are currently active**. All affected plugins then report `plugin not installed`, the channel owned by a plugin (Signal here) loses its owner, and the Codex harness and llama.cpp embeddings disappear. The Gateway itself stays up, so the only symptom is a config warning in the log.
### Steps to reproduce
1. Linux, npm global install, Gateway as a systemd user service, external plugins `@openclaw/signal`, `@openclaw/acpx`, `@openclaw/codex`, `@openclaw/llama-cpp-provider` installed at 2026.9.3.
2. Upgrade core to 2026.9.8, stop the Gateway, then `openclaw plugins update <spec>@2026.9.8` for each plugin (they install into `~/.openclaw/npm/projects/<plugin>__openclaw-generation__g-<hash>`). `openclaw plugins list` shows all four at 2026.9.8.
3. Run `openclaw doctor --fix --non-interactive`, then start the Gateway.
4. ~30-70 s after `gateway ready`, the log shows `cleaned 4 retained npm plugin generation(s)`; the generation directories are gone and every plugin reports `plugin not installed`.
### Expected behavior
Cleanup removes only superseded generations; the generation each plugin's install record points to is never pruned.
### Actual behavior
The active generations are pruned. Subsequent loads fail with ENOENT on the deleted generation path, and `Config warnings: plugins.entries.<id>: plugin not installed` repeats every ~8 s.
Recovery that works: with the Gateway stopped, `openclaw plugins install @openclaw/<plugin>@2026.9.8 --force` for each plugin, then start. Those installs land in the base project directory (`~/.openclaw/npm/projects/openclaw-<plugin>-<hash>`, no generation suffix) and survive restarts. In later Ansible-driven runs the same day, all four again reported `plugin not installed` shortly after Gateway start and needed the same forced reinstall each time.
### OpenClaw version
2026.9.8 (fc23bc8), upgraded from 2026.9.3
### Operating system
Ubuntu (Linux x64), Node v24.18.0 via nvm
### Install method
npm global (nvm prefix), Gateway as systemd user service (`openclaw gateway install`)
### Model
openai/gpt-5.5 (not model-specific)
### Provider / routing chain
openai via Codex runtime (`agentRuntime.id: codex`); not provider-specific
### Logs
Sanitized excerpt (UTC, paths shortened to `~`):
```
04:38:37Z INFO Installing to ~/.openclaw/npm/projects/openclaw-signal-dc91bfc2e9__openclaw-generation__g-f444dbe5ea6fb86d…
04:38:44Z INFO Installing to ~/.openclaw/npm/projects/openclaw-acpx-052d680d6d__openclaw-generation__g-b2d597498c795005…
04:39:17Z INFO Installing to ~/.openclaw/npm/projects/openclaw-codex-8902d781d4__openclaw-generation__g-8b0d230c729a2812…
04:39:33Z INFO Installing to ~/.openclaw/npm/projects/openclaw-llama-cpp-provider-15b2d859e6__openclaw-generation__g-088efb5279c78a6b…
04:55:02Z INFO gateway ready
04:55:19Z ERROR [plugins] codex failed during load from ~/.openclaw/npm/projects/openclaw-codex-8902d781d4__openclaw-generation__g-8b0d230c729a2812/node_modules/@openclaw/codex/dist/index.js: Error: Native plugin companion changed before admission completed
04:55:36Z INFO cleaned 4 retained npm plugin generation(s)
04:58:13Z INFO gateway ready
04:58:30Z ERROR [plugins] acpx failed during load from ~/.openclaw/npm/projects/openclaw-acpx-052d680d6d__openclaw-generation__g-b2d597498c795005/node_modules/@openclaw/acpx/dist/index.js: Error: ENOENT: no such file or directory, stat '~/.openclaw/npm/pro…
04:58:34Z WARN Config warnings: plugins.entries.acpx: plugin not installed: acpx; plugins.entries.codex: plugin not installed: codex; plugins.entries.llama-cpp: plugin not installed: llama-cpp; plugins.entries.signal: plugin not installed: signal
```
For context: a rehearsal `doctor --fix` on a copy of this state (before the real upgrade) reported `Retired stale managed npm plugin generation(s); they will be pruned after the gateway restarts`, listing only the *previous* (2026.9.3) project directories -- so the retirement list looked correct; on the real host the post-start cleanup nonetheless removed the new 2026.9.8 generations.
### Impact and severity
Silent loss of channel ownership (inbound messages accepted, nothing answers), of the Codex agent harness, and of local embeddings (memory search falls back to keyword-only) after every plugin update, with no failed unit or exit code. We now detect it by asserting `openclaw memory status` has no `plugin not installed` lines after start and force-reinstalling.
Codex review: this still needs some work. _Reviewed October 3, 2026, 8:19 PM ET / October 4, 2026, 00:19 UTC._
**Summary**
Keep open: the report identifies an official-release failure with concrete cleanup logs and lost plugin functionality. Current safeguards already shipped in the affected release, so their presence does not demonstrate a fix.
Reproducibility: unclear. the report supplies a concrete released-version upgrade sequence and failure logs, but no failing current-main execution was established; source already protects recorded active paths.
**Regression provenance**
Possible regression — suspected (failure trace). No predecessor PR is attributed.
**Next step**
Capture redacted install paths, marker contents, and resolved CLI/service state directories across update, Doctor, and cleanup before selecting a repair.
<details>
<summary>Review details</summary>
Best possible solution:
Keep active and running plugin generations authoritative through cleanup, with visible recovery guidance when installation state disagrees.
Do we have a high-confidence way to reproduce the issue?
Unclear: the report supplies a concrete released-version upgrade sequence and failure logs, but no failing current-main execution was established; source already protects recorded active paths.
Is this the best way to solve the issue?
Unclear: forced reinstall is useful reported recovery, but a permanent fix must first identify where the active-generation records or cleanup inputs diverge.
AGENTS.md: found and applied where relevant.
Remaining risk / open question:
- The incident lacks the effective Gateway install records, startup paths, and retention-marker contents immediately before deletion; a current-main failing mechanism and narrow repair remain unestablished.
Codex review notes: model internal, reasoning medium; reviewed against [cbd103def002](https://github.com/openclaw/openclaw/commit/cbd103def002ba9f91def95e157ceb7ff6840379).
</details>
<details>
<summary>Label changes</summary>
Label changes:
- add `P1`: The reported released-version upgrade disables a real channel and agent runtime while the Gateway remains running.
- add `impact:message-loss`: The report describes Signal losing its plugin owner and accepting inbound messages without answers.
- add `impact:auth-provider`: Deletion reportedly removes the selected agent harness and local embedding provider.
- add `issue-rating: 🦐 gold shrimp`: Current issue advisory state selects this label.
- add `clawsweeper:needs-info`: Current issue advisory state selects this label.
- add `clawsweeper:no-new-fix-pr`: Current issue advisory state selects this label.
- add `clawsweeper:needs-maintainer-review`: Current issue advisory state selects this label.
Label justifications:
- `P1`: The reported released-version upgrade disables a real channel and agent runtime while the Gateway remains running.
- `impact:message-loss`: The report describes Signal losing its plugin owner and accepting inbound messages without answers.
- `impact:auth-provider`: Deletion reportedly removes the selected agent harness and local embedding provider.
</details>
<details>
<summary>Evidence reviewed</summary>
What I checked:
- **Concrete released-version incident:** The captured report names 2026.9.8 at fc23bc8, an update → Doctor → Gateway startup sequence, four generation paths, the cleanup count, subsequent ENOENT failures, and successful forced-reinstall recovery. No human discussion supplied an additional diagnosis.
- **Current cleanup protection:** Cleanup clears installed-record caches and supplies both startup paths and freshly loaded install-record paths to the retention owner. ([`src/gateway/server-retained-plugin-cleanup.ts:17`](https://github.com/openclaw/openclaw/blob/cbd103def002ba9f91def95e157ceb7ff6840379/src/gateway/server-retained-plugin-cleanup.ts#L17), [cbd103def002](https://github.com/openclaw/openclaw/commit/cbd103def002ba9f91def95e157ceb7ff6840379))
- **Deletion invariant:** A managed project requires retention markers and verified ownership before deletion; any supplied active path inside that project prevents removal. The unresolved question is why the reported active generations were absent from effective protection or otherwise became eligible. ([`src/plugins/managed-npm-retention.ts:278`](https://github.com/openclaw/openclaw/blob/cbd103def002ba9f91def95e157ceb7ff6840379/src/plugins/managed-npm-retention.ts#L278), [cbd103def002](https://github.com/openclaw/openclaw/commit/cbd103def002ba9f91def95e157ceb7ff6840379))
- **Affected release already contains safeguards:** GitHub contents inspection at the reported release SHA returned the same cleanup adapter, including cache clearing and combined startup/current installation paths. This is existing protection, not evidence of a subsequent fix. ([`src/gateway/server-retained-plugin-cleanup.ts:17`](https://github.com/openclaw/openclaw/blob/fc23bc864e4553c2d215e479eeec47b67a0bf943/src/gateway/server-retained-plugin-cleanup.ts#L17), [fc23bc864e45](https://github.com/openclaw/openclaw/commit/fc23bc864e4553c2d215e479eeec47b67a0bf943))
- **Existing boundary regressions:** The Gateway cleanup tests preserve startup and desired packages while deleting an obsolete package in project and legacy layouts. Doctor generation tests preserve a ledger-selected generation while marking and removing the stale flat installation. These were read, not executed, and do not reproduce the reported service upgrade. ([`src/gateway/server-retained-plugin-cleanup.test.ts:39`](https://github.com/openclaw/openclaw/blob/cbd103def002ba9f91def95e157ceb7ff6840379/src/gateway/server-retained-plugin-cleanup.test.ts#L39), [cbd103def002](https://github.com/openclaw/openclaw/commit/cbd103def002ba9f91def95e157ceb7ff6840379))
- **Established update contract:** The update documentation says commands performed without a running Gateway save changes for its next startup. The requested persistence is existing behavior, not a new feature. Public docs: [`docs/cli/plugins/uninstall-and-update.md`](https://docs.openclaw.ai/cli/plugins/uninstall-and-update). ([`docs/cli/plugins/uninstall-and-update.md:13`](https://docs.openclaw.ai/cli/plugins/uninstall-and-update), [cbd103def002](https://github.com/openclaw/openclaw/commit/cbd103def002ba9f91def95e157ceb7ff6840379))
Likely related people:
- **steipete:** Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
- **vincentkoc:** Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
</details>
<details>
<summary>How this review workflow works</summary>
- ClawSweeper keeps one durable marker-backed review comment per issue or PR.
- Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
- A fresh review can be triggered by eligible `@clawsweeper re-review` comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
- PR/issue authors and users with repository write access can comment `@clawsweeper re-review` or `@clawsweeper re-run` on an open PR or issue to request a fresh review only.
- Maintainers can also comment `@clawsweeper review` to request a fresh review only.
- Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
- Maintainer-only repair and merge flows require explicit commands such as `@clawsweeper autofix`, `@clawsweeper automerge`, `@clawsweeper fix ci`, or `@clawsweeper address review`.
- Maintainers can comment `@clawsweeper explain` to ask for more context, or `@clawsweeper stop` to stop active automation.
</details>
<!-- clawsweeper-review-version item=164629 reviewed_at=2026-10-04T00:19:47.198Z sha=na source_revision=6fba847d69621df2b10bc44ee33a6337349490f1316013ce35a67b74dac8770f lease_owner=github-run-37164380479-1 lease_comment_id=5974913695 v=1 -->
<!-- clawsweeper-review item=164629 -->
I tried to reproduce this today against current `main` and 2026.9.8 and could not: with two generations where the newer one is the active install record (advanced 2026.9.3 → 2026.9.8 through the real install-record owner), Gateway start-up cleanup removed only the superseded generation, even with a stale retention marker on the active one and no startup-path protection. The cleanup owner (`managed-npm-retention.ts`, called from the retained-plugin cleanup at start) re-reads the persisted update ledger after clearing its caches, and those safeguards are in 2026.9.8 too.
So something about your layout makes the Gateway see a different "active" generation than the CLI wrote. The likeliest candidates are the CLI and the service resolving different state directories, or the plugin update being recorded under a different install path than the service loads. Could you capture, right after `openclaw plugins update … ` and BEFORE the next Gateway start (redact anything private):
```bash
openclaw plugins list --json
```
```bash
systemctl --user show openclaw-gateway.service -p Environment -p ExecStart
```
```bash
ls -la ~/.openclaw/npm/projects/ && cat ~/.openclaw/npm/projects/*/.openclaw-retention* 2>/dev/null
```
plus the `OPENCLAW_STATE_DIR`/`HOME` the service runs with versus your shell. With the effective state directories, persisted install paths, and the retention-marker contents from just before the deletion I can find the owner quickly.
Thank you for digging into this, @steipete, and apologies: **this was our deployment, not OpenClaw.** Your reproduction was right.
We run OpenClaw from an Ansible role with a post-install "disk cleanup" task that removes `~/.openclaw/npm` wholesale. It was written when that directory was only a cache. In current releases it's the external-plugin install root, so the task deleted every installed plugin on every run, *after* the Gateway had started.
On the incident night, that task ran at 04:58:28Z. The `ENOENT` on the acpx generation is at 04:58:30Z. The `cleaned 4 retained npm plugin generation(s)` line at 04:55:36Z was your cleanup correctly removing the superseded 2026.9.3 generations.
Confirmed today with the captures you asked for:
- A sequence of stop, `doctor --fix --non-interactive`, stop and start lost nothing over 10 minutes.
- All four plugins stayed `loaded` in their install-record paths.
- CLI and service resolve the same install (nvm prefix), the same `HOME`, and no `OPENCLAW_STATE_DIR`.
- No retention markers were present.
- The role's task log shows `changed` for that delete on every run that later reported `plugin not installed`.
We've removed the task. Closing as not an OpenClaw bug. Sorry for the noise, and thanks for the fast and careful triage.
Thanks for closing the loop so cleanly, @morikplay, and no apology needed; a cleanup task written when `~/.openclaw/npm` was only a cache is exactly the kind of thing that turns into a plugin wipe once that directory became the external plugin install root. Your captures match the reproduction: the "cleaned 4 retained npm plugin generation(s)" line was the superseded 2026.9.3 generations being retired correctly, and the ENOENT two seconds after your Ansible task ran was the task, not the Gateway. Closing as not an OpenClaw defect. One follow-up I will take from this: the docs should say plainly that `~/.openclaw/npm` is durable install state, not a cache, so other deployment scripts do not make the same assumption.