fix: guard against division by zero in cursor provider and deferred refresh race condition#123
Open
PolatOzaslan21 wants to merge 1 commit into
Conversation
…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
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR fixes two critical bugs identified during a code audit:
1. Division by zero in Cursor provider (
src/providers/cursor.ts)When
includedApiUsdis0, the expressionusage.api.costUsd / includedApiUsdproducesInfinityorNaN, resulting in-Infinity%orNaN%displayed in the quota toast.Fix: Added a guard
includedApiUsd > 0 ? ... : 0to safely return0when the budget is zero.Additionally,
getEffectiveCursorIncludedApiUsdinsrc/lib/cursor-pricing.tswas rejecting0as a valid override (> 0instead of>= 0). This meant users could not explicitly set a zero API budget. Changed to>= 0so0is accepted as a valid override value.2. Race condition in deferred quota refresh (
src/plugin.ts)In
showQuotaToast, thefinallyblock resetsstate.inFlight = falseby looking up the state from thedeferredQuotaRefreshesMap. However, during thetryblock,reconcileDeferredQuotaRefreshmay clear the old state and schedule a new one — creating a new state object in the Map. Thefinallyblock would then find this new state and incorrectly set itsinFlighttofalse, even though it was never set totrueby the current invocation.Fix: Added an identity check
state === pendingDeferredin thefinallyblock to ensure we only resetinFlighton the same state object that was marked at the start of the function.Test
Added a test case in
tests/providers.cursor.test.tsthat verifies the cursor provider correctly handlescursorIncludedApiUsd: 0without producingNaNorInfinity.Changes
src/providers/cursor.tspercentRemainingagainst division by zerosrc/lib/cursor-pricing.ts0as validoverrideUsd(>= 0)src/plugin.tsfinallyto prevent race conditiontests/providers.cursor.test.tsVerification
pnpm run typecheckpassespnpm run buildpassespnpm test -- tests/providers.cursor.test.ts tests/lib.cursor-usage.test.ts— all 14 tests pass