Skip to content

Error handling

npubcash uses two error formats: one for its authenticated JSON API and one for the LNURL protocol.

Authenticated API errors

Most /api/v2 failures return an HTTP error status and a JSON body:

json
{
  "error": true,
  "message": "Human-readable error description"
}

Common statuses include:

StatusMeaning
400 Bad RequestRequired input or headers are missing or invalid.
401 UnauthorizedThe NIP-98 event or JWT is missing, invalid, or expired.
402 Payment RequiredA username purchase requires a Cashu payment.
404 Not FoundThe requested resource does not exist.
409 ConflictThe requested username is already taken.
500 Internal Server ErrorThe server could not complete the request.

A 402 response also exposes an encoded Cashu payment request through the X-Cashu response header.

LNURL errors

LNURL discovery and invoice failures follow the LNURL error shape:

json
{
  "status": "ERROR",
  "reason": "Invalid recipient."
}

Some recipient errors intentionally use HTTP 200 because LNURL clients inspect the response body. Do not rely on the HTTP status alone for LNURL requests.

NIP-05 lookup failures also currently use HTTP 200, with the authenticated API's { "error": true, "message": "..." } shape. Check the body returned by /.well-known/nostr.json before reading its names and relays maps.

SDK errors

SDK HTTP methods throw ApiError, which exposes both message and statusCode. A username request that receives 402 throws PaymentRequiredError; its paymentRequest property contains the decoded Cashu payment request.

ts
import {
  ApiError,
  NPCClient,
  PaymentRequiredError,
} from "npubcash-sdk";

export async function loadQuotes(client: NPCClient) {
  try {
    return await client.getAllQuotes();
  } catch (error) {
    if (error instanceof PaymentRequiredError) {
      console.error("Cashu payment required", error.paymentRequest);
    } else if (error instanceof ApiError) {
      console.error(
        `npubcash request failed (${error.statusCode})`,
        error.message,
      );
    }
    throw error;
  }
}

The SDK authentication provider automatically requests another JWT after its cached token expires. Create a new client/provider only when changing the server, signer, or authentication strategy.

Direct fetch

ts
async function getQuotes(token: string) {
  const response = await fetch("https://npub.cash/api/v2/wallet/quotes", {
    headers: { Authorization: `Bearer ${token}` },
  });

  const body = await response.json();
  if (!response.ok) {
    throw new Error(body.message ?? `HTTP ${response.status}`);
  }
  return body.data.quotes;
}

For WebSocket connections, handle error messages and transport errors through the SDK's optional onError callback. Reconnect by creating a new subscription; the current subscription manager does not reconnect automatically.

Released under the MIT License.