Thanks to visit codestin.com
Credit goes to github.com

Skip to content

refactor: abort unreadable config saves with a typed StorageError - #587

Merged
ndycode merged 2 commits into
mainfrom
claude/audit-68-config-typed-errors
Jun 11, 2026
Merged

ndycode merged 2 commits into
mainfrom
claude/audit-68-config-typed-errors

Conversation

@ndycode

@ndycode ndycode commented Jun 11, 2026

Copy link
Copy Markdown
Owner

Summary

What Changed

lib/config.ts — one new unreadableConfigSaveError(configPath, errorMessage) helper replaces the three inline throws (env-path branch inside the ESTALE CAS, unified path, legacy path). It returns a StorageError carrying:

  • the byte-identical historical message (existing rejects.toThrow("unreadable") assertions and CLI output are unchanged),
  • code: "UNREADABLE" (following the existing non-errno precedent of "UNKNOWN" in the storage layer),
  • path = the offending config file,
  • an actionable hint ("Fix or remove the unreadable config file, then retry the save."),
  • the read-classifier's message as cause for debuggability.

The typed throw still propagates immediately through the ESTALE withRetry (its code is not ESTALE → non-retryable), exactly as before.

docs/reference/error-contracts.md — adds the savePluginConfig guarantee to the "Typed Error Classes" section that #586 introduced (branch rebased onto post-#586 main so the section exists here).

test/config-save.test.ts — new test pinning the class, path, code, and exact message through the real savePluginConfig env path, alongside the three existing unreadable tests which pass unchanged.

Validation

  • npm run typecheck (also in the pre-commit hook)
  • npx eslint lib/config.ts test/config-save.test.ts --max-warnings=0
  • npm test -- test/config-save.test.ts — 21/21 (the three pre-existing unreadable-abort tests pass without modification, proving message compatibility)
  • Full-suite canary on clean post-merge-storm main completed just before this branch: exactly the 58-name environment baseline, zero new names
  • npm run build deferred to CI

Docs and Governance Checklist

  • docs/reference/error-contracts.md updated in the same PR as the behavior it documents
  • No user-visible message, command, or path changed; StorageError extends CodexError extends Error, so all instanceof Error handling is unaffected

Risk and Rollback

  • Risk level: low — error class tightened, message and throw conditions identical; the only observable difference is richer metadata on the error object.
  • Rollback plan: revert the two commits.

https://claude.ai/code/session_01XNtnkLbBiXZxfQQYLMpucB

note: greptile review for oc-chatgpt-multi-auth. cite files like lib/foo.ts:123. confirm regression tests + windows concurrency/token redaction coverage.

Greptile Summary

replaces three inline new Error(...) throws in savePluginConfig with a typed StorageError via a new unreadableConfigSaveError factory, satisfying audit §4.3. the "UNREADABLE" code is not in the ESTALE withRetry's retryableCodes list, so retry behavior is unchanged; message text is byte-identical, preserving existing test assertions.

  • lib/config.ts: adds the unreadableConfigSaveError factory returning a StorageError with code, path, hint, and cause; all three unreadable-abort throw sites updated.
  • test/config-save.test.ts: new test verifies instanceof StorageError, path, code, and message through the env-path branch with an EBUSY read spy.
  • docs/reference/error-contracts.md: one-line contract guarantee added for savePluginConfig's new typed error.

Confidence Score: 4/5

safe to merge; the only observable change is richer metadata on the thrown error object — message text, throw conditions, and retry behavior are all identical to before

the three throw sites are straightforward substitutions, the ESTALE retry loop correctly non-retries the new StorageError (code "UNREADABLE" is absent from retryableCodes: ["ESTALE"]), and the new test covers the core contract. the savePluginConfig JSDoc block is left orphaned above the helper, which is a cosmetic gap but doesn't affect runtime behavior.

lib/config.ts — the original savePluginConfig JSDoc block is now detached from the function; worth fixing before merging to keep IDE hover docs accurate

Important Files Changed

Filename Overview
lib/config.ts three bare Error throws replaced by a typed StorageError via a small factory; retry behavior through the ESTALE withRetry loop is unaffected ("UNREADABLE" is not in retryableCodes: ["ESTALE"]); the original savePluginConfig JSDoc is now orphaned above the helper and detached from the function
test/config-save.test.ts new test pins instanceof StorageError, path, code, and message through the real env-path branch; env var cleanup is handled by the existing afterEach restore loop; hint and cause are not asserted but that's a minor gap
docs/reference/error-contracts.md one-line guarantee added for savePluginConfig's StorageError; matches the implementation exactly

Sequence Diagram

sequenceDiagram
    participant C as Caller
    participant S as savePluginConfig
    participant R as readConfigRecordForSave
    participant F as unreadableConfigSaveError
    participant W as withRetry (ESTALE)

    C->>S: savePluginConfig(patch)
    S->>W: withRetry([ESTALE], op)
    W->>R: readConfigRecordForSave(path)
    alt "status === "unreadable""
        R-->>W: "{ status: "unreadable", errorMessage }"
        W->>F: unreadableConfigSaveError(path, msg)
        F-->>W: "StorageError(code="UNREADABLE")"
        note over W: "UNREADABLE" not in ["ESTALE"] → rethrow immediately
        W-->>S: throws StorageError
        S-->>C: throws StorageError
    else "status === "ok" or "missing""
        R-->>W: config record
        W->>W: merge + write
        W-->>S: void
        S-->>C: void
    end
Loading

Fix All in Codex

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
lib/config.ts:850-881
**JSDoc block orphaned from `savePluginConfig`**

inserting `unreadableConfigSaveError` between the existing JSDoc and the `savePluginConfig` declaration means TypeScript/IDEs now attach the `@param configPatch` / `@returns void` block to the helper (as the immediately-preceding comment) and `savePluginConfig` itself has no JSDoc at all. the fix is to move the `unreadableConfigSaveError` function (with its own JSDoc) above the savePluginConfig documentation block, or move the savePluginConfig JSDoc block to sit directly above its `export async function` declaration.

Reviews (1): Last reviewed commit: "docs: record the savePluginConfig typed-..." | Re-trigger Greptile

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@ndycode, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 13 minutes and 53 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more credits in the billing tab to continue.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 30732640-5622-4180-abb8-aa2c827eec8e

📥 Commits

Reviewing files that changed from the base of the PR and between 1d6c3e0 and 7544582.

📒 Files selected for processing (3)
  • docs/reference/error-contracts.md
  • lib/config.ts
  • test/config-save.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/audit-68-config-typed-errors
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch claude/audit-68-config-typed-errors

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

claude added 2 commits June 11, 2026 07:50
Second §4.3 slice (M11), unblocked by #585's merge: the three
'Aborting config save because <path> is unreadable.' throws in
savePluginConfig now go through one unreadableConfigSaveError helper
returning StorageError with the config path, an UNREADABLE code, an
actionable hint, and the classifier's message as the cause. Messages
are byte-identical, so existing rejects.toThrow("unreadable")
assertions and CLI output are unchanged.

New test pins the class, path, code, and exact message through the real
savePluginConfig env path.

https://claude.ai/code/session_01XNtnkLbBiXZxfQQYLMpucB
@ndycode
ndycode force-pushed the claude/audit-68-config-typed-errors branch from 54da0c0 to 7544582 Compare June 11, 2026 07:50
@ndycode
ndycode merged commit 652a562 into main Jun 11, 2026
2 checks passed
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