Stripe | Financial Infrastructure to Grow Your Revenue

Stripe | Financial Infrastructure to Grow Your Revenue

4466 articles

Handle errors


Private preview

Handle errors Private preview

Learn how to handle common errors during extension runs.

This guide covers how to handle errors when your script calls external endpoints using endpointFetch. Before you begin, make sure you’ve created an extension and set up an endpoint to invoke.

Note

Currently, Stripe only supports invoking endpoints from custom workflow actions. See Build a custom action with a script.

You don’t need to import endpointFetch because it’s a global function injected by the Stripe runtime. It’s available at runtime only, not in tests or local builds. It throws exceptions on non-2xx HTTP responses, network failures, or misconfigurations.

Handle errors in a script

You can handle recoverable HTTP errors directly in your script. In this example, the script catches a 404 response and falls back to creating the missing resource, while allowing non-recoverable errors such as the related setting and the related setting to propagate.

try {
 const result = await endpointFetch({
 endpoint: 'com.my_script.send_notifications',
 path: '/api/notifications',
 method: 'POST',
 body: JSON.stringify({
 message: `Payment received from ${customInput.name}`,
 }),
 });

 const data = JSON.parse(result.body);
 return { success: true, ts: data.ts };
} catch (e) {
 if (e.status === 404) {
 const result = await endpointFetch({
 endpoint: 'com.my_script.send_notifications',
 path: '/api/contacts',
 method: 'POST',
 body: JSON.stringify({ id: customInput.id, email: customInput.email }),
 });
 return { contact: JSON.parse(result.body), created: true };
 }

 throw e;
}

Success responses (2xx)

The endpoint returns a 2xx status code.

In your script, you get a result object:

const result = await endpointFetch({
 endpoint: 'slack_api',
 path: '/chat.postMessage',
 method: 'POST',
 body: JSON.stringify({ channel: '#billing-alerts', text: 'Invoice paid' }),
});

// result = { ok: true, status: 200, body: '{"ok":true,"channel":"C123","ts":"1234567890.123456"}' }

Typical handling:

const result = await endpointFetch({
 endpoint: 'slack_api',
 path: '/chat.postMessage',
 method: 'POST',
 body: JSON.stringify({ channel: '#billing-alerts', text: 'Invoice paid' }),
});

const data = JSON.parse(result.body);
return { posted: true, ts: data.ts };

Client errors (4xx)

The endpoint rejects the request with a 4xx status code, such as 400 Bad Request, 403 Forbidden, 404 Not Found, or 429 Rate Limited.

In your script, endpointFetch throws:

await endpointFetch({
 endpoint: 'slack_api',
 path: '/chat.postMessage',
 method: 'POST',
 body: JSON.stringify({ channel: '#nonexistent', text: 'hello' }),
});

// Throws: Error {
// message: "Endpoint returned HTTP 400",
// code: "EXT_BAD_REQUEST",
// status: 400,
// body: '{"ok":false,"error":"channel_not_found"}',
// stack: "..."
// }

The error includes:

  • e. message : "Endpoint returned HTTP {status}"
  • e. code : An EXT _ * code mapped from the HTTP status
  • e. status : The numeric HTTP status code
  • e. body : The response body string, or null if the endpoint returns an empty response
  • e. stack : The standard V8 stack trace

Typical handling:

try {
 const result = await endpointFetch({
 endpoint: 'slack_api',
 path: '/chat.postMessage',
 method: 'POST',
 body: JSON.stringify({ channel, text: message }),
 });
 const data = JSON.parse(result.body);
 return { success: true, ts: data.ts };
} catch (e) {
 if (e.status === 429) {
 return { success: false, retriable: true, reason: 'rate_limited' };
 }
 if (e.status) {
 const error = e.body ? JSON.parse(e.body) : {};
 return { success: false, status: e.status, error: error.error };
 }
 throw e; // re-throw non-HTTP errors
}

Server errors (5xx)

The endpoint reports a server-side failure with a 5xx status code, such as HTTP 500, 502 Bad Gateway, or 503 Service Unavailable.

In your script, the error matches 4xx errors: an exception with code, status, body, and stack. body might be null if the endpoint returns an empty response.

await endpointFetch({
 endpoint: 'slack_api',
 path: '/chat.postMessage',
 method: 'POST',
 body: JSON.stringify({ channel: '#billing-alerts', text: 'Invoice paid' }),
});

// Throws: Error {
// message: "Endpoint returned HTTP 503",
// code: "EXT_RESOURCE_UNAVAILABLE",
// status: 503,
// body: null,
// stack: "..."
// }

Typical handling:

try {
 const result = await endpointFetch({
 endpoint: 'slack_api',
 path: '/chat.postMessage',
 method: 'POST',
 body: JSON.stringify({ channel, text: message }),
 });
 return { success: true };
} catch (e) {
 if (e.status >= 500) {
 return { success: false, retriable: true, status: e.status };
 }
 throw e;
}

Network errors

Network errors occur when Stripe accepts the request, but the call to the endpoint fails. That includes:

  • DNS resolution failure for the endpoint host
  • Connection refused by the endpoint
  • Endpoint request timeout

If you don’t catch the error, the script run fails with a runtime_error error code (non-retryable).

In your script:

await endpointFetch({
 endpoint: 'slack_api',
 path: '/chat.postMessage',
 method: 'POST',
 body: JSON.stringify({ channel: '#billing-alerts', text: 'Invoice paid' }),
});

// Throws: Error { message: "Network error", code: "ERR_NETWORK", stack: "..." }

Properties:

  • e. message : Always "Network error"
  • e. code : "ERR _ the related setting"
  • e. stack : The standard V8 stack trace

Hostnames and low-level connection details aren’t exposed to scripts for security reasons.

Configuration errors

Configuration errors occur if Stripe can’t find a resource the script references. That includes:

  • The endpoint name in the app manifest doesn’t exist
  • The script ID isn’t found

Fix the manifest or deployment. If uncaught, the script run fails with a bad_request error code (non-retryable).

In your script:

await endpointFetch({
 endpoint: 'nonexistent_api',
 path: '/test',
 method: 'GET',
});

// Throws: Error { message: "Endpoint not found: nonexistent_api", code: "ERR_CONFIG" }

The error has the following properties:

  • e. message : Describes what’s missing (for example, "Script not found: scp _ 123" , "Endpoint not found: slack _ api" , or "Resource not found" when details aren’t available)
  • e. code : "ERR _ the related setting"

There are no e.status or e.body properties. This isn’t an HTTP response.

In this case, let the error propagate. It’s a configuration or deployment issue, not something to handle at runtime in the script.

Error scenarios summary

Stripe doesn’t automatically retry failed endpointFetch calls. To retry, handle it in your script.

ScenarioException?e.codee.statuse.bodyRetryable?
2xx successNoN/AN/AN/AN/A
400 Bad RequestYesthe related setting400Error body or nullNo
401 UnauthorizedYesthe related setting401Error body or nullNo
403 ForbiddenYesthe related setting403Error body or nullNo
404 Not FoundYesthe related setting404Error body or nullNo
429 Rate LimitedYesthe related setting429Error body or nullYes
Other 4xxYesthe related setting4xxError body or nullNo
HTTP 500Yesthe related setting500Error body or nullNo
503 UnavailableYesthe related setting503Error body or nullYes
504 TimeoutYesthe related setting504Error body or nullYes
Other 5xxYesthe related setting5xxError body or nullNo
Network failureYesthe related settingN/AN/ANo
Configuration errorYesthe related settingN/AN/ANo

Next steps

Last verified 2026-09-24

Is this helpful?