Skip to content

Commit 9d2812f

Browse files
committed
fix(backup): skip delivery-queue json files from backup snapshots
Real-world run of `openclaw backup create` on v2026.5.7 reproduced an ENOENT race on a delivery-queue entry: Error: ENOENT: no such file or directory, lstat '$STATE_DIR/delivery-queue/3fac5e46-...json' Files under `{stateDir}/delivery-queue/**/*.json` are transient IPC state for the messaging delivery queue: they are created and consumed continuously and routinely vanish between tar's directory read and `lstat()`. They have no restoration value — capturing a partial tail only risks tar corruption without benefit. Extends the volatile filter with the same `isUnder` + `hasExtension` rule style as the existing sessions / cron-runs / logs rules, and keeps the JSDoc rule list in sync. Tests: 44/44 green across backup-volatile-filter + backup-create. Built-by: Vulcan 🔨
1 parent 053c118 commit 9d2812f

2 files changed

Lines changed: 32 additions & 0 deletions

File tree

src/infra/backup-volatile-filter.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,32 @@ describe("isVolatileBackupPath", () => {
5555
expect(isVolatileBackupPath(`${stateDir}/logs/../notes.jsonl`, plan)).toBe(false);
5656
});
5757

58+
it("treats delivery-queue json files under stateDir as volatile", () => {
59+
expect(
60+
isVolatileBackupPath(
61+
`${stateDir}/delivery-queue/3fac5e46-42dc-4230-a725-51c203830b4f.json`,
62+
plan,
63+
),
64+
).toBe(true);
65+
});
66+
67+
it("treats nested delivery-queue json files under stateDir as volatile", () => {
68+
expect(
69+
isVolatileBackupPath(
70+
`${stateDir}/delivery-queue/subdir/3fac5e46-42dc-4230-a725-51c203830b4f.json`,
71+
plan,
72+
),
73+
).toBe(true);
74+
});
75+
76+
it("does not treat non-json delivery-queue files as volatile", () => {
77+
expect(isVolatileBackupPath(`${stateDir}/delivery-queue/README.md`, plan)).toBe(false);
78+
});
79+
80+
it("does not treat delivery-queue json outside stateDir as volatile", () => {
81+
expect(isVolatileBackupPath(`/tmp/delivery-queue/file.json`, plan)).toBe(false);
82+
});
83+
5884
it("normalizes Windows-style separators before anchor checks", () => {
5985
const winStateDir = "C:\\openclaw\\state";
6086
const winPlan = { stateDirs: [winStateDir] };

src/infra/backup-volatile-filter.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export type VolatileFilterPlan = {
4848
* - `{stateDir}/sessions/**`/`*.{jsonl,log}`
4949
* - `{stateDir}/cron/runs/**`/`*.log`
5050
* - `{stateDir}/logs/**`/`*.{jsonl,log}`
51+
* - `{stateDir}/delivery-queue/**`/`*.json`
5152
* - Any file matching `*.{sock,pid,tmp,lock}` anywhere under scope
5253
*/
5354
export function isVolatileBackupPath(absolutePath: string, plan: VolatileFilterPlan): boolean {
@@ -80,6 +81,11 @@ export function isVolatileBackupPath(absolutePath: string, plan: VolatileFilterP
8081
if (isUnder(filePosix, logsRoot) && hasExtension(filePosix, [".jsonl", ".log"])) {
8182
return true;
8283
}
84+
85+
const deliveryQueueRoot = path.posix.join(stateDirPosix, "delivery-queue");
86+
if (isUnder(filePosix, deliveryQueueRoot) && hasExtension(filePosix, [".json"])) {
87+
return true;
88+
}
8389
}
8490

8591
return false;

0 commit comments

Comments
 (0)