stripe
semver
>=21.0.0postconditions16functions7last verified2026-06-15coverage score86%Postconditions — what we check
- constructEvent · wrong-webhook-parsing-methoderrorWhenconstructEvent() called with a v2 event-notification payload, or with a payload generated by a webhook destination that expects parseEventNotification()Throws
StripeError — v21 added an explicit guard that throws when the wrong parsing method is usedRequired handlingUse parseEventNotification() for v2 thin events (EventNotifications) and constructEvent() for v1 events. Inspect the webhook destination type before choosing which method to call. Wrap both in try/catch and surface a 400.costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - constructEventAsync · wrong-webhook-parsing-methoderrorWhenconstructEventAsync() called with a v2 event-notification payload, or payload from a destination that expects parseEventNotification()Throws
StripeError — v21 added an explicit guard that throws when the wrong parsing method is usedRequired handlingUse parseEventNotification() for v2 thin events and constructEventAsync() for v1.costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[1] - token · oauth-invalid-granterrorWhenAuthorization code is expired, already used, doesn't exist, or live/test mode mismatch with API keyThrows
StripeInvalidGrantError (extends StripeOAuthError)Required handlingCatch StripeInvalidGrantError specifically OR catch the StripeOAuthError parent. Surface a user-facing "authorization expired, please reconnect" message; redirect the user back through the OAuth start URL. DO NOT retry — codes are single-use.costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - token · oauth-invalid-clienterrorWhenclient_id does not belong to your platform, or an API key is required but not providedThrows
StripeInvalidClientError (extends StripeOAuthError)Required handlingCatch StripeInvalidClientError or the StripeOAuthError parent. This is a configuration error — log and alert ops, do not retry, do not show user a redirect (the OAuth setup is broken on your side).costcriticalin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[3] - token · oauth-invalid-requesterrorWhenRequired OAuth parameter missing, or unsupported parameter/value providedThrows
StripeOAuthInvalidRequestError (extends StripeOAuthError)Required handlingCatch StripeOAuthInvalidRequestError or the StripeOAuthError parent. Indicates malformed request from your code — fix and redeploy, do not retry.Sources[3] - token · oauth-invalid-scopeerrorWhenInvalid scope value provided in OAuth requestThrows
StripeInvalidScopeError (extends StripeOAuthError)Required handlingCatch StripeInvalidScopeError or the StripeOAuthError parent. Fix the scopes list in your OAuth start URL.Sources[3] - token · oauth-unsupported-grant-typeerrorWhenUnsupported grant_type parameter in OAuth requestThrows
StripeUnsupportedGrantTypeError (extends StripeOAuthError)Required handlingCatch StripeUnsupportedGrantTypeError or the StripeOAuthError parent. Use authorization_code or refresh_token; consult Stripe Connect docs.Sources[3] - deauthorize · oauth-invalid-granterrorWhenStripe user id is invalid or the access token has already been revokedThrows
StripeInvalidGrantError (extends StripeOAuthError)Required handlingCatch StripeInvalidGrantError or the StripeOAuthError parent. Surface a message that the account is already disconnected; do not retry. - deauthorize · oauth-invalid-clienterrorWhenclient_id does not belong to your platformThrows
StripeInvalidClientError (extends StripeOAuthError)Required handlingCatch StripeInvalidClientError or the StripeOAuthError parent. Configuration error — fix client_id, do not retry.Sources[3] - parseEventNotification · parse-event-notification-signature-failederrorWhenWebhook payload signature is invalid — wrong signing secret, tampered payload, or expired timestamp (outside DEFAULT_TOLERANCE of 300s). Signature check happens via webhooks.signature.verifyHeader() called inside parseEventNotification.Throws
StripeSignatureVerificationError (extends StripeError)Required handlingWrap stripe.parseEventNotification() in try-catch. Catch StripeSignatureVerificationError specifically and return HTTP 400 to Stripe. DO NOT process the event if signature verification fails — the payload cannot be trusted. Unlike constructEvent, parseEventNotification processes v2 thin events, so the same security requirement applies with equal urgency.costhighin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - parseEventNotification · parse-event-notification-wrong-payload-typeerrorWhenA v1 event payload (event.object === 'event') is mistakenly passed to parseEventNotification(). The implementation explicitly checks for this and throws a plain Error with message: "You passed a webhook payload to stripe.parseEventNotification, which expects an event notification. Use stripe.webhooks.constructEvent instead." Conversely, passing a v2 thin event to constructEvent() throws the inverse guard. Both guards were added in stripe-node v21 (PR #2618).Throws
Error (plain JavaScript Error, not StripeError)Required handlingUse parseEventNotification() for v2 thin events (event_payload: 'thin' destination) and stripe.webhooks.constructEvent() for v1 events (event_payload: 'snapshot'). Inspect the webhook destination configuration before choosing the parsing method. A catch block on StripeError alone will NOT catch this — it's a plain Error. Wrap in generic try-catch: catch (e) { if (e instanceof Error) return res.status(400)... }costmediumin prodimmediate exceptionusers seeservice unavailablevisibilityvisible - fetchRelatedObject · fetch-related-object-no-try-catcherrorWhenNetwork failure (DNS, TCP, TLS), API authentication error, or server error while fetching the related object from Stripe's v2 API. The HTTP GET is made using the same request pipeline as all other Stripe API calls, meaning all StripeConnectionError (network), StripeAuthenticationError (bad API key), StripeAPIError (5xx), and StripeRateLimitError conditions apply.Throws
StripeConnectionError | StripeAuthenticationError | StripeAPIError | StripeRateLimitErrorRequired handlingAlways await eventNotification.fetchRelatedObject() inside a try-catch block. Note that null is returned (not thrown) when there is no related_object — this is a silent case that must also be checked: if (!related) return; or a nullish check before using the result. Network failures in webhook handlers cause silent failures if the catch block re-throws without surfacing a 500.costmediumin prodimmediate exceptionusers seelost datavisibilitysilent - fetchRelatedObject · fetch-related-object-temporary-session-expirederrorWhenThrows TemporarySessionExpiredError (rawType: 'temporary_session_expired') when the StripeContext attached to the event notification has expired. StripeContext is a v21 addition that scopes v2 API requests to a specific event — if the session window has passed, the context-authenticated request is rejected. This error class is NEW in v21 and does NOT extend StripeRateLimitError — a catch block for StripeRateLimitError will NOT catch it. Confirmed from cjs/Error.js: generateV2Error dispatches 'temporary_session_expired' to new TemporarySessionExpiredError (rawType: 'temporary_session_expired').Throws
TemporarySessionExpiredError (extends StripeError, rawType: 'temporary_session_expired')Required handlingCatch TemporarySessionExpiredError explicitly. The standard pattern is to re-fetch the event from the v2 events API: stripe.v2.core.events.retrieve(id). Do NOT retry the same fetchRelatedObject() call — the session is permanently expired. Failing to handle this will silently drop the event's side-effect processing.costmediumin prodimmediate exceptionusers seelost datavisibilitysilent - fetchRelatedObject · fetch-related-object-null-uncheckedwarningWhenfetchRelatedObject() returns Promise<null> (not throws) when the EventNotification has no related_object field. This is a confirmed behavior from cjs/stripe.core.js: if (!eventNotification.related_object) { return Promise.resolve(null); }. Code that destructures or accesses properties on the result without a null check will throw a TypeError at runtime.Throws
TypeError (null property access — NOT a StripeError, thrown by caller code)Required handlingAlways check the return value before use: const related = await notification.fetchRelatedObject(); if (!related) return; // notification has no related object This is NOT a Stripe SDK error but a caller-side TypeError from null access. Particularly dangerous in typed TypeScript where the return type is typed as Promise<T> and developers assume non-null.costlowin prodimmediate exceptionusers seeservice unavailablevisibilityvisibleSources[7] - fetchEvent · fetch-event-no-try-catcherrorWhenNetwork failure, authentication error, or server error while fetching the full event from /v2/core/events/{id}. Same error types apply as all Stripe v2 API calls: StripeConnectionError (network), StripeAuthenticationError (invalid key), StripeAPIError (5xx), StripeRateLimitError (v1 path) or RateLimitError (v2 path).Throws
StripeConnectionError | StripeAuthenticationError | StripeAPIError | StripeRateLimitError | RateLimitErrorRequired handlingAlways await eventNotification.fetchEvent() inside a try-catch block. A common anti-pattern is calling fetchEvent() only when needed (e.g., inside a switch/case handler) without wrapping the call — the outer catch block from parseEventNotification() does NOT cover async calls made after the synchronous parse.costmediumin prodimmediate exceptionusers seelost datavisibilitysilent - fetchEvent · fetch-event-temporary-session-expirederrorWhenThrows TemporarySessionExpiredError when the StripeContext session has expired. Same mechanism as fetchRelatedObject — the context is scoped to the event notification and expires after a window. Confirmed from cjs/Error.js generateV2Error dispatch. This is the same new v21-specific error class as in fetchRelatedObject.Throws
TemporarySessionExpiredError (extends StripeError, rawType: 'temporary_session_expired')Required handlingCatch TemporarySessionExpiredError explicitly. Re-fetch the event using stripe.v2.core.events.retrieve(eventId) with standard Stripe API authentication instead of the context-scoped fetchEvent() call. Log the expired context for monitoring — frequent expiry indicates delayed webhook processing.costmediumin prodimmediate exceptionusers seelost datavisibilitysilent
Sources
Every postcondition cites at least one of these. Numbered to match the footnotes above.
- [1]github.com/stripe/stripe-nodehttps://github.com/stripe/stripe-node/pull/2618
- [2]github.com/stripe/stripe-nodehttps://github.com/stripe/stripe-node/blob/v21.0.0/CHANGELOG.md
- [3]raw.githubusercontent.com/stripe/stripe-nodehttps://raw.githubusercontent.com/stripe/stripe-node/v21.0.0/src/Error.ts
- [4]stripe.com/docs/connecthttps://stripe.com/docs/connect/oauth-reference
- [5]stripe.com/docs/connecthttps://stripe.com/docs/connect/oauth-reference#post-deauthorize
- [6]raw.githubusercontent.com/stripe/stripe-nodehttps://raw.githubusercontent.com/stripe/stripe-node/v21.0.1/cjs/Webhooks.js
- [7]raw.githubusercontent.com/stripe/stripe-nodehttps://raw.githubusercontent.com/stripe/stripe-node/v21.0.1/cjs/stripe.core.js
- [8]raw.githubusercontent.com/stripe/stripe-nodehttps://raw.githubusercontent.com/stripe/stripe-node/v21.0.1/cjs/RequestSender.js
- [9]raw.githubusercontent.com/stripe/stripe-nodehttps://raw.githubusercontent.com/stripe/stripe-node/v21.0.1/cjs/Error.js
- [10]raw.githubusercontent.com/stripe/stripe-nodehttps://raw.githubusercontent.com/stripe/stripe-node/v21.0.1/types/Errors.d.ts
- [11]docs.stripe.com/contexthttps://docs.stripe.com/context
Need a different package?
Request a profile