Skip to content

fix(chatgpt): remove the unguarded markOpenAiChatGptError write - #7847

Merged
iscekic merged 2 commits into
mainfrom
drain/chatgpt-unguarded-error-write
Oct 21, 2026
Merged

iscekic merged 2 commits into
mainfrom
drain/chatgpt-unguarded-error-write

Conversation

@iscekic

@iscekic iscekic commented Oct 17, 2026 •

Copy link
Copy Markdown
Collaborator

Change

markOpenAiChatGptError in packages/web-shared/src/lib/ai-gateway/openai-chatgpt/store.ts read the stored connection without a row lock and then wrote the terminal error unconditionally: it disabled the row and cleared the stored token set. Its guarded siblings never had this hazard — the refresh path writes markOpenAiChatGptConnectionErrored inside the transaction that holds the SELECT ... FOR UPDATE row lock, and markOpenAiChatGptNotEligible re-reads the row under lock and writes only when the stored access token is still the refused one. A reconnect that landed between the unguarded read and write could be disabled and emptied by the stale failure.

The helper no longer has any production caller: the refresh path moved inside the row-locked transaction, and refusal handling goes through markOpenAiChatGptNotEligible. Rather than guard a dead export, this change deletes it, so no future caller can reuse the unguarded write. Tests that exercised only the deleted helper are dropped; the cleared-state test in store.test.ts now stores an errored payload directly.

Verification

  • Repo-wide search confirms no remaining reference to markOpenAiChatGptError anywhere (source or tests).
  • New real-database suite refresh-terminal-race.test.ts pins the guarded path: it parks a failing refresh inside its token exchange, issues a reconnect while the row lock is held, and proves the reconnect does not land until the transaction commits — so the terminal write cannot erase it, and the reconnected connection ends enabled with its own tokens. Removing the row lock from resolveInsideLock makes the reconnect land and the test fail, so the test discriminates.
  • The same suite covers the terminal path end to end: the connection whose refresh token OpenAI rejected ends disabled with its tokens cleared and the reconnect message, on a real database.
  • pnpm test -- openai-chatgpt in apps/web: 10 suites, 139 tests, all passing.
  • pnpm --filter @kilocode/web-shared run typecheck: clean (both tsconfigs).
  • pnpm --filter web run typecheck: clean (pre-existing warnings only).
  • pnpm format + git diff --check: clean.

Proxy answers

The owner's proxy gave these answers in the owner's place.

  1. Question: markOpenAiChatGptError currently has no production callers (its only former caller, the refresh path, moved inside the row-locked transaction). The brief says to guard it with a FOR UPDATE re-read + access-token comparison. Which shape should the fix take?
    Answer: Delete the dead export
    Confidence: 0.50 · Precedent: Speed and least work: 'Replace old paths instead of keeping them' (session 2190fcfd0f42: 'I am leaning heavily toward just ripping things out'); Product scope: 'Trust the code over documents' (session 269375d6e466). Deletion removes the unguarded write, so it fixes the class. Confirm in end-to-end proof that the refresh path's locked transaction already guards against the race.
  2. Question: If guarding: what should callers pass to identify the connection they used?
    Answer: Access token (recommended)
    Confidence: 0.60 · Precedent: Speed and least work: 'Do the least work possible with no drop in quality' (session 7f3470e0ba6f). This applies only if the guard is kept; the access token mirrors markOpenAiChatGptNotEligible and needs no new stored id.
  3. Question: Where should the behavioral tests live?
    Answer: New real-DB suite file (recommended)
    Confidence: 0.55 · Precedent: Proof and verification: 'Require valid end-to-end proof on every PR' (session 55c9c80332a7). Only a real database can prove the lock behavior. If the export is deleted, add the race regression tests against the refresh path in the real-DB suite.

Both conditions attached to answer 1 are met: the refresh path's locked transaction is proven against the race in the new real-database suite, and the race regression tests live in that suite.

markOpenAiChatGptError read the connection without a row lock and wrote
the terminal error unconditionally, so a reconnect landing between the
read and the write could be disabled and emptied by a stale failure.
It has no production callers left (the refresh path marks errors inside
its row-locked transaction, and refusals go through the locked,
token-checked markOpenAiChatGptNotEligible), so the export is deleted
instead of guarded. Tests that exercised only this helper are dropped;
the cleared-state test now stores an errored payload directly.
@iscekic iscekic added drain Work item from the findings drain janitor:performance-reliability Janitor performance-reliability findings labels Oct 17, 2026
@kilo-code-bot

kilo-code-bot Bot commented Oct 17, 2026 •

Copy link
Copy Markdown
Contributor

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

The incremental commit adds only a real-database regression suite; the row-lock assertion is deterministic (the refresh transaction holds the lock until the test releases it), and the deleted markOpenAiChatGptError still has no references anywhere in the repo.

Files Reviewed (3 files)
  • packages/web-shared/src/lib/ai-gateway/openai-chatgpt/refresh-terminal-race.test.ts - new real-DB suite; parses against refresh.ts/store.ts and covers both the terminal write and a reconnect blocked by the row lock
  • packages/web-shared/src/lib/ai-gateway/openai-chatgpt/store.ts - targeted re-check; markOpenAiChatGptError remains removed and markOpenAiChatGptConnectionErrored is only called with a transaction that holds the lock
  • packages/web-shared/src/lib/ai-gateway/openai-chatgpt/store.test.ts - targeted re-check; no stale references to the removed helper

Note: Promise.withResolvers is available on the repo's Node >=24 runtime and the esnext lib, and the primary pool (max: 10) lets the reconnect acquire its own connection, so the blocked upsert is waiting on the row lock rather than the pool.

Previous Review Summary (commit 169b0f6)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 169b0f6)

Status: No Issues Found | Recommendation: Merge

Executive Summary

The PR removes the unguarded markOpenAiChatGptError write and its dedicated tests; the deleted export has no remaining callers and the surviving refresh/refusal paths already write under the row lock, so the change is a safe removal with no correctness or security gaps found.

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 0
Files Reviewed (2 files)
  • packages/web-shared/src/lib/ai-gateway/openai-chatgpt/store.ts - 0 issues (removed dead markOpenAiChatGptError; markOpenAiChatGptConnectionErrored and markOpenAiChatGptNotEligible unchanged and still transact under the row lock)
  • packages/web-shared/src/lib/ai-gateway/openai-chatgpt/store.test.ts - 0 issues (dropped tests exercised only the deleted helper; the rewritten "clears a previous error state" test pushes an errored row directly and asserts the save clears it)

Note: a repo-wide search (including ignored files) confirms no remaining reference to markOpenAiChatGptError, and refresh.ts imports markOpenAiChatGptConnectionErrored rather than the removed helper, so no runtime call site is broken.


Reviewed by deepseek-v4.1-flash · Input: 54.9K · Output: 11.2K · Cached: 740.2K

Review guidance: REVIEW.md from base branch main

The deleted markOpenAiChatGptError was unguarded; the guarded terminal
write lives in the refresh transaction. Add a real-database suite that
holds the failing refresh in its token exchange and proves a reconnect
issued in that window does not land until the transaction commits, so
the terminal write cannot erase it. Removing the row lock from
resolveInsideLock makes the reconnect land and the test fail.
@iscekic iscekic added the merge:human Substantive functional risk requires human review. label Oct 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

drain Work item from the findings drain janitor:performance-reliability Janitor performance-reliability findings merge:human Substantive functional risk requires human review.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants