#22648: fix(plugin-sdk): re-throw non-ENOENT errors in readJsonFileWithFallback
size: XS
trusted-contributor
Cluster:
Config File Security Enhancements
## Summary
`readJsonFileWithFallback` silently returns the fallback value for **every** filesystem
error, not just `ENOENT`. This means permission errors (`EACCES`), filesystem corruption,
and other unexpected conditions are swallowed — callers receive the default value and
have no way to detect or recover from the real failure.
## Bug
```ts
// Before — lines 19-22
if (code === "ENOENT") {
return { value: fallback, exists: false };
}
return { value: fallback, exists: false }; // ← same fallback for EACCES, EPERM, etc.
```
The `if (code === "ENOENT")` guard is correct, but the unconditional fallback on
line 22 makes it a no-op: non-ENOENT errors fall through and silently return the
same `{ value: fallback, exists: false }`.
## Fix
```ts
// After
if (code !== "ENOENT") {
throw err;
}
return { value: fallback, exists: false };
```
Invert the guard so that only `ENOENT` returns the fallback. All other errors
propagate to the caller.
## Affected callers
- `src/pairing/pairing-store.ts` — pairing code persistence
- `extensions/msteams/src/store-fs.ts` — MS Teams channel store
- Any plugin importing `readJsonFileWithFallback` from `openclaw/plugin-sdk`
All callers currently assume errors are surfaced; this fix makes that assumption hold.
## Alignment with upstream direction
Recent commits `083298ab` and `ec419895` hardened ENOENT handling in the memory
module. This PR applies the same pattern to the plugin-sdk's shared JSON helper.
## Risk
Low — the only behavioral change is that non-ENOENT errors now throw instead of
being silently ignored. This is strictly safer: callers that were unknowingly
receiving stale/default data will now see the real error.
## Test plan
- [x] `npx oxfmt --check src/plugin-sdk/json-store.ts` — pass
- [x] `npx oxlint src/plugin-sdk/json-store.ts` — 0 warnings, 0 errors
- [x] `npx vitest run src/plugin-sdk/ --config vitest.unit.config.ts` — 22 tests pass (1 pre-existing import failure in index.test.ts unrelated to this change)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
Fixed a critical bug in `readJsonFileWithFallback` where non-`ENOENT` filesystem errors (permission errors, corruption, etc.) were silently swallowed and returned the fallback value instead of propagating to the caller. The fix inverts the error guard to re-throw all non-`ENOENT` errors, ensuring callers can detect and handle real failures.
- Changed error handling logic from checking `code === "ENOENT"` followed by an unconditional fallback return, to `code !== "ENOENT"` with a `throw`, ensuring only missing-file errors return the fallback
- Affects callers in `src/pairing/pairing-store.ts` and `extensions/msteams/src/store-fs.ts` which wrap this function for JSON persistence
- Low risk change that strictly improves safety by exposing previously hidden errors
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge with no concerns
- The fix is minimal, correct, and strictly safer than the existing code. It only changes behavior for error cases that were previously being silently ignored (non-ENOENT errors), ensuring they now propagate properly. The logic is straightforward and the change has been tested according to the PR description.
- No files require special attention
<sub>Last reviewed commit: 115fe23</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#19510: fix(config): preserve configured values on invalid config validatio...
by yash27-lab · 2026-02-17
76.0%
#17887: refactor: consolidate atomic JSON writes into shared writeJsonAtomic
by iyoda · 2026-02-16
75.1%
#22993: fix(delivery): guard JSON.parse in failDelivery to prevent silent i...
by adhitShet · 2026-02-21
72.8%
#23672: fix(resilience): guard JSON.parse of external process output with t...
by kevinWangSheng · 2026-02-22
72.4%
#21039: Fix npm-spec plugin installs when npm pack output is empty
by graysurf · 2026-02-19
72.1%
#16542: fix(sessions): use atomic temp+rename write on Windows
by aldoeliacim · 2026-02-14
71.7%
#18966: fix(config): downgrade unknown bundled plugin references to warnings
by moxunjinmu · 2026-02-17
71.4%
#21967: Harden Slack allow-from resolution against undefined catch crash
by graysurf · 2026-02-20
71.3%
#17896: fix(config): warn instead of silently swallowing chmod failure on c...
by PlayerGhost · 2026-02-16
71.1%
#23337: fix: move @discordjs/opus to optionalDependencies [AI-assisted 🤖]
by davidmckenzie · 2026-02-22
71.1%