Skip to content

fix(cursor): add REST fallback for Free accounts and fix team inference#497

Open
deep5deep wants to merge 2 commits into
robinebers:mainfrom
deep5deep:fix/cursor-free-usage-fallback
Open

fix(cursor): add REST fallback for Free accounts and fix team inference#497
deep5deep wants to merge 2 commits into
robinebers:mainfrom
deep5deep:fix/cursor-free-usage-fallback

Conversation

@deep5deep
Copy link
Copy Markdown

@deep5deep deep5deep commented May 23, 2026

This pull request addresses an issue where Cursor Free tier users fail the usage probe in OpenUsage.

Problems Addressed:

  1. Missing limit in Connect API: For Cursor Free tier accounts, the internal Connect RPC endpoint (GetCurrentPeriodUsage) does not return a limit property in planUsage.
  2. False Positive Team Account: The plugin's isTeamAccount check matched Free tier users because spendLimitUsage.pooledLimit is 0 (which is a number). This caused the plugin to incorrectly attempt a Team/REST fallback.
  3. No REST Usage Limits: The REST request-based fallback threw a request-based: no usage data available exception because Free tier users have no fast requests limits in the REST API.

Changes:

  • Added REST & Stripe Fallbacks: Added a fallback mechanism inside the probe function so that if the main Connect API request fails (e.g., due to TLS/HTTP2 handshake issues on some systems), it gracefully resolves the Free plan name and total requests using the REST and Stripe APIs.
  • Fixed Team Inference check: Changed the check so that pooledLimit must be greater than 0 to prevent matching Free accounts:
    (su && typeof su.pooledLimit === 'number' && su.pooledLimit > 0)

Summary by cubic

Adds REST + Stripe fallback in the Cursor usage probe to show plan and usage for Free accounts when the Connect API fails or returns no limits, and fixes team inference so Free users aren’t treated as Team. Also consolidates Stripe calls behind a shared helper.

  • New Features

    • Fallback resolves plan from Stripe membership and request usage from REST; shows progress if a limit exists, otherwise shows used count or “Free tier active”.
    • Adds student verification label when available.
  • Bug Fixes

    • Team detection now requires pooledLimit > 0, preventing Free accounts from matching Team.
    • Replaces hard failures on Connect API errors with a soft fallback and warnings, avoiding probe failures for Free users.

Written for commit 49f9f9c. Summary will update on new commits. Review in cubic

This addresses an issue where Cursor Free tier users fail the usage probe due to: 1) missing limit property in Connect RPC response, 2) false positive team-inferred account when pooledLimit is 0. Adds a robust REST/Stripe API fallback to retrieve Free tier usage details.
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 1 file

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread plugins/cursor/plugin.js
@deep5deep deep5deep force-pushed the fix/cursor-free-usage-fallback branch from fab047d to 49f9f9c Compare May 23, 2026 14:39
@validatedev validatedev requested a review from Copilot May 23, 2026 21:57
@validatedev
Copy link
Copy Markdown
Collaborator

@codex review

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the Cursor plugin usage probe to better support Free-tier accounts and to be more resilient when the Connect API can’t provide limits or can’t be reached.

Changes:

  • Introduces a Stripe + REST fallback path when the Connect API call throws (e.g., transport/TLS/HTTP2 issues), returning a best-effort plan label and request usage.
  • Refactors Stripe fetching into a shared helper and reuses it for both balance and membership info.
  • Fixes team-account inference to require pooledLimit > 0, preventing Free accounts from being misdetected as Team.
Comments suppressed due to low confidence (1)

plugins/cursor/plugin.js:424

  • didRefresh is assigned in the refresh callback but never read anymore. Either remove it, or use it to decide whether to surface a post-refresh failure vs. falling back (to avoid dead code and keep error handling intentional).
    let connectApiFailed = false
    let didRefresh = false
    try {
      usageResp = ctx.util.retryOnceOnAuth({
        request: (token) => {

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread plugins/cursor/plugin.js
@@ -432,9 +438,64 @@
},
})
} catch (e) {
Comment thread plugins/cursor/plugin.js
Comment on lines +449 to +455
const lines = []
let plan = "Free"

if (stripeInfo) {
const membership = stripeInfo.membershipType || stripeInfo.individualMembershipType || "free"
plan = ctx.fmt.planLabel(membership) || "Free"

Comment thread plugins/cursor/plugin.js
Comment on lines +441 to +448
ctx.host.log.warn("Connect API failed, entering REST fallback: " + String(e))
connectApiFailed = true
}

if (connectApiFailed) {
const stripeInfo = fetchStripeInfo(ctx, accessToken)
const requestUsage = fetchRequestBasedUsage(ctx, accessToken)

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 49f9f9cf91

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread plugins/cursor/plugin.js
Comment on lines +441 to +442
ctx.host.log.warn("Connect API failed, entering REST fallback: " + String(e))
connectApiFailed = true
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Re-throw auth/connect failures instead of forcing fallback

This catch block treats every exception from retryOnceOnAuth as a Connect API outage and unconditionally switches to the Free-tier fallback path. In cases like refresh throwing "Token expired"/"Session expired" or transient network failures, the probe no longer fails loud; it can return a synthetic success (Free tier active) when REST/Stripe calls also fail, which misreports logged-out/offline users as active Free accounts and suppresses re-login/error handling.

Useful? React with 👍 / 👎.

@zergzorg
Copy link
Copy Markdown
Contributor

zergzorg commented May 24, 2026

I took a local pass over the unresolved review feedback and reproduced the issue.

Current PR head fails the Cursor plugin test file:

  • bun run test plugins/cursor/plugin.test.js --run
  • result before fix: 5 failures, all caused by the new connectApiFailed catch swallowing auth/request errors and returning the REST/Stripe fallback instead of throwing.

The smallest fix I found is to remove the synthetic fallback for exceptions from retryOnceOnAuth and preserve the existing error contract. The existing request-based fallback later in the probe still handles missing/limitless planUsage; this just prevents auth/network/request exceptions from being misreported as a successful Free result.

Patch that passes locally:

diff --git a/plugins/cursor/plugin.js b/plugins/cursor/plugin.js
@@
-    let connectApiFailed = false
     let didRefresh = false
@@
           } catch (e) {
             ctx.host.log.warn("connectPost failed: " + String(e))
-            throw e
+            if (didRefresh) {
+              throw "Usage request failed after refresh. Try again."
+            }
+            throw "Usage request failed. Check your connection."
           }
@@
     } catch (e) {
-      ctx.host.log.warn("Connect API failed, entering REST fallback: " + String(e))
-      connectApiFailed = true
-    }
-
-    if (connectApiFailed) {
-      ...
-      return { plan: plan, lines: lines }
+      if (typeof e === "string") throw e
+      ctx.host.log.error("usage request failed: " + String(e))
+      throw "Usage request failed. Check your connection."
     }

After applying that locally, plugins/cursor/plugin.test.js passes: 59/59.

This should close the Codex P1 and the related Copilot comments: auth/session string errors are re-thrown, non-string request wrapper errors stay user-facing failures, and the PR no longer defaults to Free on a failed Stripe/REST fallback after a Connect exception.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants