Skip to content
Fuse
Esc
navigateopen⌘Jpreview
On this page

Errors

FuseApiError, FuseError, error codes, and the is* helpers

Every SDK method that talks to the orchestrator can reject. Non-2xx responses throw a FuseApiError, built from the server’s structured error envelope; anything else (a network failure, a missing global fetch, a body that fails to decode) throws a FuseError instead. Branch on the code, not the message:

import { isNotFound, isConflict, FuseApiError } from "@folsom/fuse";

try {
  await client.environments.get("missing");
} catch (err) {
  if (isNotFound(err)) {
    // handle the 404 case
  } else if (err instanceof FuseApiError) {
    console.error(err.code, err.status, err.requestId);
  }
}

err.requestId, err.status, and err.details give you the full picture when you need more than a yes/no check, requestId correlates with server-side logs.

Code-checking helpers

function isNotFound(err: unknown): boolean
function isConflict(err: unknown): boolean
function isUnauthorized(err: unknown): boolean
function isInvalidArgument(err: unknown): boolean
function isUnavailable(err: unknown): boolean
function isFuseApiError(err: unknown): err is FuseApiError

Each reports whether err is a FuseApiError with the matching code. Prefer these over comparing .code strings directly, and definitely over parsing err.message, which is meant for logs, not branching logic. isFuseApiError is a type guard, narrowing err to FuseApiError when true.

The server also returns a seventh code, unimplemented (501, when a provider has no guest to exec into or attach to), with no matching is* helper yet, check err.code === "unimplemented" directly until one is added.

FuseApiError and FuseError

class FuseApiError extends Error {
  readonly status: number;
  readonly code: string;
  readonly details?: Record<string, string>;
  readonly requestId?: string;
  readonly body?: string;
}

class FuseError extends Error {
  constructor(message: string, options?: { cause?: unknown });
}

FuseError covers transport, configuration, or decoding failures, not an API error: constructing a client with no baseUrl, a missing global fetch with no fetch option supplied, or a response body that fails to parse.

Full code list

Constant Wire value Typical status
ErrorCode.NotFound not_found 404
ErrorCode.Conflict conflict 409
ErrorCode.InvalidArgument invalid_argument 400
ErrorCode.Unauthorized unauthorized 401, 403, or 502
ErrorCode.Unavailable unavailable 503
ErrorCode.Internal internal 500 or 502
(none yet) unimplemented 501
(none yet) payload_too_large 413
(none yet) route_not_found 404
(none yet) forbidden 403

The last two are easy to mistake for a code that has a helper, and neither is matched by one: route_not_found (a wrong host or port, or an endpoint this server does not mount) is not matched by isNotFound(), and forbidden (a CIDR-allowlist rejection) is not matched by isUnauthorized(). Check err.code directly for both.

errorFromResponse

async function errorFromResponse(res: Response): Promise<FuseApiError>

The lower-level function every service method calls internally on a non-2xx response. You won’t normally call this directly.

Was this page helpful?