Errors

Developers · Hashry Pay

Errors

Every error code, and what to do about it. Looking for a symptom, such as a card form that never loads? Start with Troubleshooting.

In the browser: HashryPayError

The browser packages throw a HashryPayError, or pass it to onError. Check for one with isHashryPayError(error), then read its code. Its message never holds the embed_url or a token, so it is safe to log.

codeMeaning
secret_in_browserAn option looks like a secret key. Move it to your server and rotate the key.
invalid_optionAn option is missing, or has the wrong type or range.
invalid_embed_urlembedUrl is not a valid URL.
host_not_allowedembedUrl is not on a Hashry Pay host. Mount only the embed_url Hashry returned.
load_timeoutThe card form sent no ready message in time. Arrives in onError.
status_not_foundThe status token is wrong, expired or for another payment (404).
status_failedThe status endpoint refused the request for another reason.
status_timeoutpollStatus ran longer than timeoutMs.
abortedYour AbortSignal stopped pollStatus.

In React, a bad option throws during mount. Catch it with an error boundary.

From the API

A refusal has a JSON body. Read it like this:

  • The content type is application/problem+json or application/json. Read both the same way.
  • The reason code is in code. If there is no code, it is in type: a short code, or a path that ends in one.
  • A validation error has no detail. Each bad field is a key that holds a list of messages.
A refusal
HTTP/1.1 400 Bad Request
Content-Type: application/json

{
  "type": "embed_origin_not_allowed",
  "title": "Payment request refused.",
  "status": 400,
  "detail": "That embed origin is not on this integration's allowed list."
}
A key sent from a browser
HTTP/1.1 403 Forbidden

{ "detail": "...", "code": "secret_key_used_in_browser" }
CodeStatusWhat to do
secret_key_used_in_browser403Your client secret was sent from a web browser. Call Hashry only from your server, and rotate the key.
key_revoked_browser_use403The key was revoked because it was used from a browser. Ask the merchant for a new key.
authentication_error401The client ID or secret is wrong, or the key was revoked. Check the key and the environment.
idempotency_conflict409You reused an idempotency key with different details. Use a new key for a new payment.
idempotency_key_invalid400The idempotency key is missing, or not 8 to 100 characters.
embed_origin_not_allowed400embed_origin is not on Allowed embed origins for this key's mode. Add the exact origin.
embedded_checkout_not_enabled409The embedded card form is off for this account. Ask Hashry to turn it on, or use the redirect.
origin_not_allowed400The return_url or cancel_url origin is not on Allowed return origins for this key's mode.
url_invalid400A URL is not valid. Use https (http only for localhost), with no user name or #fragment.
currency_mismatch400The currency is not the store's currency.
limit_exceeded409Over the limit for one payment, or for 24 hours, set on the channel.
intent_retry_window_closed409It is too late to retry this payment. Start a new payment with a new key.
gateway_unavailable502Nothing happened. Try again: a repeat is safe.
gateway_outcome_unknown502Hashry may have acted. Repeat with the same key, or read the payment. Never start a new one.
(none)429Too many requests. Wait, then try again.

In the server SDKs: Node and Python

Both server SDKs throw the same error classes. In Node, import them from @hashrytech/pay-node. In Python, import them from hashry_pay. Every one is a HashryError. Their messages never hold a secret, a token or an embed_url, so they are safe to log.

ErrorWhen
ApiErrorThe API refused the call or failed. The errors below say how.
InvalidRequestError400. The request is not valid. See the field errors.
AuthenticationError401. The client ID or secret is wrong, or the key was revoked.
PermissionDeniedError403. The key may not do this, for example a browser call.
NotFoundError404. No such object for this key and mode.
ConflictError409. The object is not in a state that allows this.
IdempotencyErrorThe idempotency key is missing, not valid, or reused with new details.
RateLimitError429. Too many requests. Wait the retry-after time, then try again.
ServerError5xx. Hashry or the gateway failed. Check whether the outcome is unknown.
ApiConnectionErrorNo answer. ApiTimeoutError is the timeout case.
ConfigurationErrorA bad setup, for example a test key with the live environment.
WebhookErrorA webhook failed its checks. Answer 400. reason says why.

ApiError has code, status and detail. Node also gives fieldErrors and retryAfter. Python gives field_errors and retry_after.

Node
import { ApiConnectionError, ApiError } from "@hashrytech/pay-node";

try {
  await hashry.paymentIntents.cancel(uid);
} catch (error) {
  if (error instanceof ApiError && error.code === "cancel_not_allowed") {
    // A card was already entered. Wait for the outcome instead.
  } else if (error instanceof ApiConnectionError) {
    // No answer. Try again later, or read the payment.
  } else {
    throw error;
  }
}
Note:

When the outcome is unknown, Hashry may have acted anyway. That is when outcomeUnknown (Node) or outcome_unknown (Python) is true. ApiConnectionError is always outcome unknown. Repeat the call with the same idempotency key, or read the payment. Never create a new one.

Next: fix it

Back to top

© 2026 Hashry Solutions Limited. All rights reserved.