Skip to content

fix: guard against division by zero in cursor provider and deferred refresh race condition#123

Open
PolatOzaslan21 wants to merge 1 commit into
slkiser:mainfrom
PolatOzaslan21:fix/critical-cursor-division-and-race-condition
Open

fix: guard against division by zero in cursor provider and deferred refresh race condition#123
PolatOzaslan21 wants to merge 1 commit into
slkiser:mainfrom
PolatOzaslan21:fix/critical-cursor-division-and-race-condition

Conversation

@PolatOzaslan21

Copy link
Copy Markdown

Summary

This PR fixes two critical bugs identified during a code audit:

1. Division by zero in Cursor provider (src/providers/cursor.ts)

When includedApiUsd is 0, the expression usage.api.costUsd / includedApiUsd produces Infinity or NaN, resulting in -Infinity% or NaN% displayed in the quota toast.

Fix: Added a guard includedApiUsd > 0 ? ... : 0 to safely return 0 when the budget is zero.

Additionally, getEffectiveCursorIncludedApiUsd in src/lib/cursor-pricing.ts was rejecting 0 as a valid override (> 0 instead of >= 0). This meant users could not explicitly set a zero API budget. Changed to >= 0 so 0 is accepted as a valid override value.

2. Race condition in deferred quota refresh (src/plugin.ts)

In showQuotaToast, the finally block resets state.inFlight = false by looking up the state from the deferredQuotaRefreshes Map. However, during the try block, reconcileDeferredQuotaRefresh may clear the old state and schedule a new one — creating a new state object in the Map. The finally block would then find this new state and incorrectly set its inFlight to false, even though it was never set to true by the current invocation.

Fix: Added an identity check state === pendingDeferred in the finally block to ensure we only reset inFlight on the same state object that was marked at the start of the function.

Test

Added a test case in tests/providers.cursor.test.ts that verifies the cursor provider correctly handles cursorIncludedApiUsd: 0 without producing NaN or Infinity.

Changes

File Change
src/providers/cursor.ts Guard percentRemaining against division by zero
src/lib/cursor-pricing.ts Accept 0 as valid overrideUsd (>= 0)
src/plugin.ts Identity check in finally to prevent race condition
tests/providers.cursor.test.ts New test for zero-budget scenario

Verification

  • pnpm run typecheck passes
  • pnpm run build passes
  • pnpm test -- tests/providers.cursor.test.ts tests/lib.cursor-usage.test.ts — all 14 tests pass
  • Tested against OpenCode Go (current production version)

…efresh race condition

- cursor.ts: Add includedApiUsd > 0 guard to prevent NaN/Infinity in
  percentRemaining when included API budget is zero
- cursor-pricing.ts: Accept 0 as a valid overrideUsd (>= 0 instead of > 0)
  so users can explicitly set zero API budget
- plugin.ts: Fix race condition in showQuotaToast finally block where a
  newly created deferred state could be incorrectly mutated by comparing
  the state reference against pendingDeferred before resetting inFlight
- Add test for zero includedApiUsd override scenario
@PolatOzaslan21

Copy link
Copy Markdown
Author

Closes #127 — division by zero + race condition

Refs #128 — bun:sqlite compatibility issue (follow-up PR needed)

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.

1 participant