Guides

Troubleshooting and error codes

Every client API, SDK and vendor API error code, what to tell your customer, and fixes for the most common problems.

Start with the code. The client API and the SDKs return a stable code (the message is English text for logs, not meant to be parsed), and the Events screen of the dashboard records what happened to each license. The tables below say what each code means, what to show your customer and how to fix it; the second half covers common symptoms.

Client API errors#

Returned by activate, validate and deactivate (client API), and passed through by the SDKs (Node: KeyrailApiError.code; Rust: the code of the LicenseError; Tauri: the rejection's code, or reason in the state).

HTTPcodeMeaning and likely causeTell your customerFix
400invalid_requestMalformed body, bad fingerprint, or a key that does not have the key format (typo, missing character).“This doesn’t look like a license key. Please paste it again.”Check the key as typed; keys are matched ignoring case and spaces.
404unknown_productNo product with this ID: the product ID compiled into the app is wrong.“Licensing is misconfigured. Please contact support.”Copy the Product ID from the product page again.
404unknown_keyNo license with this key for this product: a typo, or a key of another product.“This license key was not found. Check it, or find your keys in the customer portal.”Look the key up in Licenses (full key).
404not_activatedvalidate for a machine that has no activation, typically after the machine was freed from the dashboard, the portal or the app.“This computer is no longer activated. Enter your key to activate it again.”Activate again. The SDK clears its cache on this code.
403license_expiredPast validUntil: the subscription was not renewed within the grace period.“Your license has expired. Renew it to keep using the app.”Renewal through your provider, or Extend on the license page.
403license_suspendedSuspended by you, or the subscription is paused.“Your license is suspended. Contact support.”Reinstate on the license page, or resume the subscription.
403license_refundedFully refunded. A new payment lifts it.“This purchase was refunded.”Reinstate if the refund was a mistake.
403license_revokedChargeback or manual revocation. Final.“This license is no longer valid. Contact support.”Issue a new license if appropriate; a revoked one cannot come back.
403account_closedActivation only: the vendor closed its Keyrail account. Machines already activated keep validating for 30 days.“Activation is unavailable. Contact support.”Reopen the account with the emailed link, within 30 days.
409activation_limitEvery activation of the license is in use.“This key is already used on the maximum number of computers. Free one in the customer portal, or contact support.”See activation limit reached.
429rate_limitedMore than 30 calls per hour for one key and IP address, or 600 per hour for one IP.“Too many attempts. Try again later.”Don’t call the API in a loop; the SDKs refresh about once a day.
500internal_errorOur side.Nothing: the app keeps working on its stored certificate.Retried at the next check.

The SDKs treat unknown_product, unknown_key, not_activated and the four license_* codes as definitive: they clear the cached certificate and the app loses access at once. Network errors, rate_limited and internal_error are transient: the cached certificate keeps being used while it is valid.

SDK errors#

Raised by the SDKs themselves, when no usable answer came back.

codeSDKMeaning and likely causeFix
no_licenseAllNo license activated on this machine (never activated, or the cache was cleared after a definitive refusal).Ask for a key and activate. Treat it as “unlicensed”, not as an error.
network_errorAllThe service could not be reached: offline, proxy, firewall, DNS.Transient. A machine already activated keeps working offline; activation needs the network once.
timeoutNodeNo answer within timeoutMs (Rust and Tauri report a timeout as network_error).Transient, like network_error.
invalid_responseAllThe answer was not the expected JSON, for example a captive portal or a proxy page.Transient. Check the API base URL if it persists.
invalid_certificateAllActivation succeeded, but the certificate returned does not verify with the public keys in your app: a wrong public key, or a key rotation activated before your app embedded the new key. Nothing is cached.Check the public keys (as a map kid → key) against the product page, and ship the new key before activating it.
fingerprint_unavailableRust, TauriThe machine identifier could not be read.Check that the OS identifier exists (/etc/machine-id on Linux) and is readable.
invalid_configRust, TauriMissing product ID or public key, an invalid public key, an app version without a numeric major, or no HTTP transport.A build problem: fix the configuration.
io_errorRust, TauriThe cache file could not be read or written.Check the app data directory’s permissions.

Certificate verification outcomes#

When the SDK checks a certificate offline, the first failing rule gives the outcome (verification rules). Node and Tauri return it as reason with valid: false; Rust as LicenseError::Verify.

OutcomeMeaning and likely causeTell your customerFix
invalid_signatureThe signature does not verify, or its kid is not among the app’s public keys: an edited cache file, or a certificate signed with a key the app does not embed.“Your license could not be verified. Please activate again.”Embed every active and pending public key.
wrong_productThe certificate belongs to another product.“Licensing is misconfigured. Contact support.”Check the product ID in the app.
wrong_machineThe certificate was issued for another machine: a copied cache file, or a machine identifier that changed (OS reinstall, new disk, cloned image).“This computer needs to be activated again.”Activate again; free the old machine if the limit is reached.
expiredThe certificate is past its expiresAt: the app stayed offline longer than the offline period, or the license reached validUntil.“Connect to the internet so the app can renew its license.”See certificate expired offline.
not_usableThe certificate carries a status that grants no access.Same as the matching license_* code.Check the license status.
version_not_coveredThe app’s major version is above the license’s maxMajor (a perpetual license for an older major version).“Your license covers version N. Upgrade it to use this version.”Sell the upgrade, then raise Max major version with Edit on the license.
clock_tamperedThe system clock is more than one day behind the newest certificate this machine has seen.“Your computer’s date and time look wrong. Set them automatically and restart the app.”See clock problems.
malformedThe certificate cannot be parsed: a truncated or edited cache.“Please activate again.”Activate again.

Vendor API errors#

The vendor API and the dashboard use the same error shape, with these extra codes.

HTTPcodeMeaningFix
401unauthorizedMissing, invalid, revoked or expired credentials, or a closed account.Check the Authorization: Bearer kr_sk_… header; create a new key in API keys if it was revoked.
401invalid_tokenA dashboard sign-in link that is unknown, already used or older than 15 minutes.Request a new sign-in link.
402plan_limitCreating a product beyond your Keyrail plan.Upgrade in Account & usage. Other limits are soft and never block your customers.
403forbiddenAn action reserved to the account owner (export, private key export, sending domain, closing).Sign in as the owner.
403forbidden_originA dashboard write without the dashboard’s own Origin, or a “Lost your key?” request from a website that is not in the allowed list.Use the dashboard itself; add the website’s exact origin under Account & usage.
404not_foundNo such object in your account.Check the ID and the account the key belongs to.
409conflictA change to a revoked license, a duplicate plan code or integration, reinstating a license that is neither suspended nor refunded, or a sending domain held by another account.Read the message; revocation is final.
413payload_too_largeThe request body is over the size limit of the route.Send a smaller body.
429rate_limitedMore than 5,000 requests per hour for the account, or the hourly budget of license-key emails is used up (also 5 sends per license and hour).Wait, and batch your calls.

Webhook returns 401#

The provider shows failed deliveries with 401: Keyrail rejected the signature, before processing anything.

  • No secret yet. The integration shows Secret missing: paste the secret in Integrations and click Save secret.
  • Wrong secret. Paddle: copy the secret key of the notification destination that points to this webhook URL. Lemon Squeezy: the signing secret must be exactly the one you typed when creating the webhook. Paste it again to replace it (it is write-only, so you cannot read it back to compare).
  • Sandbox and live mixed up. Paddle sandbox and live are separate accounts with separate destinations and secrets: each needs its own integration with its own secret.
  • Old delivery. A Paddle signature older than 5 minutes is refused.

Once the secret is fixed, replay the failed events from your provider. A 404 instead of 401 means the URL points to no integration (a typo, or an integration you disconnected): copy the Webhook URL again from Integrations.

No license after a payment#

Open Events in the dashboard and look for the webhook.received event of the payment:

  • Ignored, unknown price: the Paddle price ID or Lemon Squeezy variant ID is not in the integration’s Price map. Add it, Save price map, then replay the event from your provider. Every price you sell (monthly, yearly, each currency) needs a row.
  • Ignored, test mode event on a live integration (or the reverse): a Lemon Squeezy test-mode order reached a live integration. Create a sandbox integration for test mode.
  • No event at all: the webhook never arrived or was refused. Check the URL in your provider and its delivery log; look for 401 (see above). In Paddle, check that the destination’s events include transaction.completed and customer.created.
  • A 500 in Paddle’s log, then a success: Paddle sent the transaction before the customer.created event, so Keyrail did not know the buyer’s email yet; Paddle retries and the license is created on the retry. Add a Paddle API key with customer.read permission to the integration to avoid the wait.
  • 429: the hourly budget of license-key emails of your Keyrail plan is used up; the provider retries later.

Customer did not receive the key#

  1. Open the license (search Licenses by email). In History, License key e-mailed to the customer means the email left Keyrail.
  2. Ask the customer to check their spam folder and the address they used at checkout (shown on the license).
  3. If your customer emails come from your own address, check that your sending domain still verifies; until it does, they go out from Keyrail’s address with your sender name.
  4. Click Send key again on the license page (at most 5 times per license and hour).

License key not e-mailed: the account was closed means the key was withheld because the account is closed. Customers can also find their keys in the customer portal, or with a “Lost your key?” form on your website (Account & usage).

Activation limit reached#

409 activation_limit means every activation of the license is used: machines per seat × seats, or the license’s override. Usually the customer replaced a computer or reinstalled the OS, which changes the machine identifier.

  • The customer frees the old machine in the customer portal, or from your app if it offers a “Deactivate” action (the SDK deactivate call).
  • You free it from the license page: Free this machine in Machines.
  • To allow more machines on this license, use Edit: more Seats, or a Machine limit override. For all licenses of a plan, raise Machines per seat in Plans.

Re-activating a machine that is already activated is not an error and uses no extra seat.

Certificate expired offline#

A certificate is valid until the earlier of validUntil and the offline period (offlineDays, 30 by default) after its last refresh. An app that stays offline longer reports expired. Connecting once is enough: the next check() refreshes the certificate if the license is still usable. If the license itself reached validUntil, the refresh fails with license_expired and the customer must renew. Choose offlineDays per product or per plan in the dashboard (Products, Plans).

Clock problems#

  • clock_tampered: the clock is more than one day behind a certificate this machine already received, for example a wrong date after a dead BIOS battery or a manual change. A refresh cannot fix it: the customer sets the clock right (automatic time) and restarts the app.
  • Clock far ahead: certificates look expired (expired) as soon as the machine’s date passes their expiresAt, even after a refresh, because their dates come from Keyrail’s clock. Again, the fix is the system clock.

Still stuck?#

Write to support@keyrail.tech with the license ID (lic_…) or the integration ID (int_…) and the time of the problem. Never send a secret API key or a webhook secret.

View as Markdown

Need help? See Troubleshooting or write to support@keyrail.tech.