Skip to content

Sirloin Errors

The errors operators and clients see most often, with code references and remediation. Domain billing errors are defined in apps/sirloin/internal/app/services/billing/domain/errors.go. Cross-cuts: security-model for auth-related errors, observability for log/trace lookup.

Most billing errors are wrapped by BillingError (Op, Entity, Err, Details) — search Axiom by Op="checkout" etc. when triaging.

Domain Errors

ErrorWhen raisedMeaningRemediationSource
subscription not foundRPCs that resolve a subscription by idCaller passed a stale/foreign id, or DB ↔ Chargebee driftVerify id in admin (strip); reconcile via cmd/scripts/wallet-recovery-*billing/domain/errors.go
invoice not foundPayment recording, dunning retryChargebee invoice id missing locallyRe-run event poller or call SubmitPaidInvoicebilling/domain/errors.go
customer not foundAny subscription opClerk user lacks Chargebee customerRecreate via checkout flowbilling/domain/errors.go
coupon not foundValidateCoupon, checkoutCoupon code invalid or removed in ChargebeeCaller should clear inputbilling/domain/errors.go
item price not foundListProducts, checkoutPlan/addon price missing in ChargebeeSync plans (cmd/scripts/backfill-purchase-prices)billing/domain/errors.go
payment not foundGetPaymentPrimer/Chargebee payment missingWait for poller; verify transactionIDbilling/domain/errors.go
invalid subscription stateCancel / reactivate / downgradeState machine refuses transitionRead billing-pitfalls; fix in adminbilling/domain/errors.go
invalid invoice statePayment recordingInvoice already paid/voidedNo action; idempotent retry safebilling/domain/errors.go
subscription is not activeUsage-gated RPCs (GenerateMedia)Sub is future, cancelled, or pausedDirect user to checkout / reactivationbilling/domain/errors.go
payment currency does not match invoice currencyPayment recordingPrimer settled in non-matching currencyManual reconcile in Chargebeebilling/domain/errors.go
service temporarily unavailableAny Chargebee/Primer callCircuit breaker openWait, retry; check provider statusbilling/domain/errors.go
rate limited by downstream serviceChargebee/Primer 429Upstream throttledBackoff; reduce poller cadence if sustainedbilling/domain/errors.go

Webhook Errors

StatusPathCauseRemediation
401 Unauthorized/webhooks/chargebee/eventsBasic auth header missing or wrongSet SIRLOIN_CHARGEBEE_WEBHOOK_USERNAME / _PASSWORD; reconfigure Chargebee endpoint creds
401 Unauthorized/webhooks/primer/disputes, /webhooks/primer/paymentsHMAC mismatch or signedAt outside 3-min skewRe-verify SIRLOIN_PRIMER_WEBHOOK_SECRET; check NTP drift on host
401 Unauthorized/webhooks/ondato/verification-statusShared-secret mismatchVerify SIRLOIN_ONDATO_WEBHOOK_SECRET
405 Method Not Allowedany webhookCaller used GETUse POST
413 Payload Too Large/webhooks/primer/disputesBody > 1 MiBTrim payload upstream; otherwise no path

Source: internal/app/services/billing/webhooks/chargebeewebhook.go, internal/app/services/billing/disputes/primerwebhook.go.

gRPC Errors

Sirloin maps domain errors to gRPC status codes at the handler edge. Common mappings (TODO(@zen): exhaustive table — derive from each handler):

gRPC codeTypical causeExamples
NOT_FOUNDDomain *NotFound errorssubscription/invoice/customer/coupon
FAILED_PRECONDITIONinvalid * state, subscription is not activeusage-gated RPCs
UNAVAILABLECircuit breaker open (ErrServiceUnavailable)Chargebee/Primer outage
RESOURCE_EXHAUSTEDErrRateLimitedupstream 429
UNAUTHENTICATEDMissing/invalid Clerk tokenbrisket / strip handlers
PERMISSION_DENIEDStrip RPC without admin roleStripService

Startup Errors

Error / logMeaningRemediation
MissingError("SIRLOIN_DATABASE_URL")DB DSN unsetSet env var; sirloin refuses to start
using default port :50051 (warn)SIRLOIN_PORT unsetAcceptable in dev
Chargebee webhook credentials not set (warn)Username or password emptyWebhooks will return 401 until set
Primer API key not set (warn)Primer checkout disabledSet key for prod
Primer webhook secret not set (warn)Primer webhooks rejectedSet secret

Source: apps/sirloin/internal/app/config/config.go.

Upstream-Propagated Errors

Chargebee and Primer errors surface inside BillingError.Err with the provider’s status / code. Recurring patterns:

Provider errorWhat it meansAction
Chargebee 404Resource not in Chargebee (returned as nil, not an error — see billing-pitfalls)Treat as *NotFound; don’t retry
Chargebee 429Rate limitedBackoff; align poller
Chargebee 5xxProvider outageCircuit breaker opens; alerts fire
Primer Authentication failedBad API key or wrong env (sandbox vs prod)Verify SIRLOIN_PRIMER_API_KEY prefix
Primer Idempotency key conflictReusing the wrong keyUse transactionID per billing-pitfalls

Resolved

  • SubmitPaidInvoice does not distinguish “already credited” from “invoice not found” at the gRPC code level. Already-processed payments are treated as success (idempotent: PurchaseExists short-circuits and returns an empty SubmitPaidInvoiceResponse). Missing payment/invoice returns NotFound. Other status mappings: Unavailable for errPrimerPaymentStillProcessing, FailedPrecondition for errPrimerPaymentFailed, Internal for everything else (apps/sirloin/internal/app/services/billing/submitpaidinvoice.go:182-:194, :351-:368, :238-:240).

TODO(@zen)

  • TODO(@zen): exhaustive gRPC code mapping per RPC.
  • TODO(@zen): full enumeration of BillingError Op values used in production.