Skip to content

fix(nodejs): write state files atomically and tolerate corruption - #109

Open
tangquanwei wants to merge 1 commit into
corespeed-io:mainfrom
tangquanwei:fix/atomic-state-writes
Open

tangquanwei wants to merge 1 commit into
corespeed-io:mainfrom
tangquanwei:fix/atomic-state-writes

Conversation

@tangquanwei

Copy link
Copy Markdown

What

A single damaged state file could permanently kill the bot. FileStorage.get() did a bare JSON.parse() and rethrew on failure, so WeChatBot.start() rejected inside poller.loadCursor() before the poll loop started:

Poll error: Unexpected token ... is not valid JSON

The state file can be damaged because set() used a plain writeFile: non-atomic, no fsync, and callers don't await it, so overlapping writes to the same path can interleave.

Fix (one file + tests)

nodejs/src/storage/file.ts:

  • Atomic writes — temp file → fsync → rename, plus writes serialized per key. Readers see either the old or the new complete file, never a partial one. delete()/clear() wait for in-flight writes.
  • Tolerant reads — strip a UTF-8 BOM; a corrupt file is quarantined (*.corrupt-<ts>) and returned as missing instead of throwing.

This removes both halves of the bug: the file is no longer produced corrupt, and even an externally damaged file can no longer take down startup.

Tests

nodejs/tests/file-storage.test.ts — atomicity, concurrent writes, BOM, NUL/corrupt quarantine, delete-vs-write race.

87 → 88 tests passing (npx vitest run), tsc --noEmit clean.

Notes

`~/.wechatbot/cursor.json` could end up zero-filled, and `FileStorage.get()`
rethrew the resulting `JSON.parse` error. That rejected `client.start()`
before the poll loop began, surfacing as:

    Poll error: Unexpected token ... is not valid JSON

Two independent fixes:

- Writes are now atomic (temp file + fsync + rename) and serialized per key,
  so a crash or overlapping write cannot leave a truncated/zero-filled file.
- Reads tolerate a UTF-8 BOM, and a corrupt file is quarantined and treated as
  missing instead of throwing, so one bad state file can no longer prevent the
  bot from starting.
Copilot AI lite review requested due to automatic review settings September 16, 2026 14:18

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants