Skip to content

fix(rpc): catch billing resolution errors in getBilling() to enable free-tier fallback - #790

Open
superlog-app[bot] wants to merge 1 commit into
stagingfrom
superlog/billing-context-autumn-key-fallback
Open

superlog-app[bot] wants to merge 1 commit into
stagingfrom
superlog/billing-context-autumn-key-fallback

Conversation

@superlog-app

@superlog-app superlog-app Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Summary

When AUTUMN_SECRET_KEY is not set (e.g., developer local environments), calls to getBillingContext and getUsage throw an unhandled Error: AUTUMN_SECRET_KEY is not set instead of returning a graceful free-tier response. This surfaces as noisy ERROR-level log entries in Superlog and likely shows an error state in the billing UI instead of defaulting to the free tier.

Root Cause

getBilling() in packages/rpc/src/orpc.ts declares return type Promise<BillingOwner | undefined> — signaling intent to return undefined when billing is unavailable — but its call to getBillingOwner() is not wrapped in any error handler. When getAutumn()createClient() throws because AUTUMN_SECRET_KEY is missing, the exception propagates up through every handler that calls context.getBilling(), bypassing the explicit free-tier fallback (if (!customerId) return { planId: "free", ... }) and the surrounding try/catch that both exist in getBillingContext for exactly this scenario.

Remediation

Wrap the getBillingOwner() calls inside getBilling() with try/catch. On failure, log a warning (not an error), leave billingCache as undefined, and set billingResolved = true to prevent retries. All calling handlers already guard with if (billing) and have free-tier fallbacks — this change lets those paths execute as intended.

Related incident: b9496c9c-385f-4d6d-8280-6d62e105d93c


Was this PR helpful? Leave feedback — goes straight to the Superlog team.


Summary by cubic

Fixes an unhandled error in getBilling() when AUTUMN_SECRET_KEY is missing, so callers now fall back to the free tier instead of throwing. The function now catches resolution errors, logs a warning, and marks billing as resolved to avoid retries.

Written for commit 6b2ec39. Summary will update on new commits.

Review in cubic

…ree-tier fallback

Delivery-Id: d84dc55ddc9d5958a0986918622006a376305d7868aeb4c89966c85fea36d5bf
Delivery-Base: staging
@vercel

vercel Bot commented Sep 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
dashboard Ready Ready Preview Sep 13, 2026 6:48pm UTC
databuddy-status Ready Ready Preview Sep 13, 2026 6:48pm UTC
1 Skipped Deployment
Project Deployment Actions Updated
documentation Skipped Skipped Sep 13, 2026 6:48pm UTC

@unkey-deploy

unkey-deploy Bot commented Sep 13, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Unkey Deploy

Name Status Preview Inspect Updated (UTC)
links (preview) Ready Visit Preview Inspect Sep 13, 2026 6:47pm

@coderabbitai

coderabbitai Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: cbfa1d65-96c5-4263-9865-bdc72ea69f2b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

@greptile-apps

greptile-apps Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR catches billing-owner resolution failures in the RPC context and memoizes an undefined result so callers can use their existing free-tier behavior.

  • Adds warning-level logging for failed billing resolution.
  • Prevents repeated billing-owner resolution attempts within the same RPC context.
  • Leaves getUsage broken when the Autumn secret is missing because that route performs another direct Autumn call.
  • Does not add coverage for the new fallback and memoization behavior.

Confidence Score: 4/5

The PR should not merge as the complete remediation described because getUsage still returns an internal error when AUTUMN_SECRET_KEY is absent.

Catching getBillingOwner failures allows getBillingContext to reach its free-tier response, but getUsage subsequently invokes getAutumn().check() and encounters the same missing-secret exception outside the new catch.

Files Needing Attention: packages/rpc/src/orpc.ts

Important Files Changed

Filename Overview
packages/rpc/src/orpc.ts Adds catch-and-memoize fallback handling for billing resolution, but the intended missing-secret remediation remains incomplete for getUsage and lacks focused tests.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[getUsage] --> B[context.getBilling]
  B --> C{Billing lookup succeeds?}
  C -->|Yes| D[Use resolved customer]
  C -->|No| E[Catch and return undefined]
  E --> F[Fall back to user customer ID]
  D --> G[getAutumn.check]
  F --> G
  G --> H{AUTUMN_SECRET_KEY set?}
  H -->|Yes| I[Return usage]
  H -->|No| J[Throw internal RPC error]
Loading

Reviews (1): Last reviewed commit: "fix(rpc): catch billing resolution error..." | Re-trigger Greptile

Comment thread packages/rpc/src/orpc.ts
Comment on lines +117 to 122
} catch (error) {
logger.warn(
{ error },
"Failed to resolve billing owner; falling back to free tier"
);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Usage fallback still fails

When AUTUMN_SECRET_KEY is missing, this catch makes context.getBilling() return undefined, but getUsage then calls getAutumn().check() directly. That call throws the same missing-secret error and is converted into an internal RPC error, so local environments still cannot retrieve usage through the intended free-tier fallback. The fallback must also cover or avoid that second Autumn call.

Comment thread packages/rpc/src/orpc.ts
Comment on lines +117 to 123
} catch (error) {
logger.warn(
{ error },
"Failed to resolve billing owner; falling back to free tier"
);
}
billingResolved = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Fallback behavior lacks coverage

The new exception-to-free-tier behavior has no direct test coverage. Existing procedure tests inject mocked getBilling implementations instead of invoking this closure, so regressions in the missing-secret fallback, warning, or resolved-state behavior could go unnoticed. Please add focused tests that cover returning undefined, logging the fallback, and avoiding another lookup on later calls.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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.

0 participants