Skip to content
Meeko
Type to search the guides and the API reference. Open it from any page with / or Ctrl K; move with the arrow keys and open a result with Enter.
Get started

Contents

On this page

ClientError

The error API, RPC and Socket clients use to reject an error response from the server, with its code, status, requestId and per-field issues.

utility Since 1.0.0 No import @meekojs/meeko/framework Source code

Se autoimporta en los componentes y en app/.

ts
import { ClientError } from "@meekojs/meeko/framework";

ClientError is what the browser and server code receive when an api.*, rpc.*, useApi, useRpcQuery, useApiMutation or useRpcMutation call ends in an error response. It is the other side of ApiError: it carries the public code and status, the request's requestId and, if present, per-field issues. A Socket channel rejected due to session or authorization also delivers a ClientError (401 UNAUTHENTICATED or 403 FORBIDDEN).

Its message is the code. An error response without a code uses HTTP_ERROR, and a successful response that is not JSON uses INVALID_RESPONSE.

Usage#

ts
import { ClientError } from "@meekojs/meeko/framework";

export async function rename(call: () => Promise<unknown>): Promise<string | null> {
  try {
    await call();
    return null;
  } catch (error) {
    if (error instanceof ClientError && error.status === 409) return "That name already exists.";
    if (error instanceof ClientError) console.warn(error.code, error.requestId);
    throw error;
  }
}

Type#

ts
class ClientError extends Error {
  constructor(
    code: string,
    status: number,
    requestId?: string,
    issues?: readonly ValidationIssue[],
  );
}

ValidationIssue

ts
interface ValidationIssue {
	readonly path: string
	readonly message: string
}

Parameters#

new ClientError(code, status, requestId?, issues?). You do not normally create it manually: Meeko clients create it.

Parameter Type Default Description
code string required The public code, which is also the message.
status number required The HTTP status.
requestId string none The request ID, the same as the x-request-id header and server logs.
issues readonly { path, message }[] none Per-field messages from a validation 400 or an ApiError with issues.

Return value#

An Error instance with name: "ClientError", code, status, requestId and issues, all read-only. issues is undefined if the response does not include them.

Examples#

Per-field messages without useForm#

ts
import { ClientError } from "@meekojs/meeko/framework";

export function fieldMessage(error: unknown, path: string): string | undefined {
  return error instanceof ClientError ? error.issues?.find((issue) => issue.path === path)?.message : undefined;
}

With useRpcMutation, fieldMessage(login.error.value, "password") returns the schema's message for that field. useForm handles this distribution for you.

Distinguish an expected error from a network failure#

fetch rejects with a TypeError when there is no network; the server error is a ClientError:

ts
import { isClientError } from "@meekojs/meeko/framework";

export function errorText(failure: unknown): string {
  if (isClientError(failure, "RATE_LIMITED")) return "Too many attempts. Wait a moment.";
  if (isClientError(failure)) return `Could not save (${failure.code}).`;
  return "Could not connect. Try again.";
}

This is the typical form of useForm's errorMessage.

Common mistakes#

Read message expecting the server's text#

message is the code, not the internal ApiError message, which never leaves the server.

Incorrectts
alert(error.message); // "EMAIL_TAKEN"
Correctts
alert(error.issues?.[0]?.message ?? "Could not create the account.");

Read properties of an unknown without checking#

In a catch or error observer, the error is unknown.

Incorrectts
try { await save(); } catch (error) { console.log(error.code); }

TS18046: 'error' is of type 'unknown'.

Correctts
try { await save(); } catch (error) { if (isClientError(error)) console.log(error.code); else throw error; }

instanceof with two copies of Meeko#

With two copies of the package on the same page (a linked package, a duplicated dependency), instanceof ClientError fails for an error from the other copy, without any warning.

Incorrectts
if (error instanceof ClientError && error.code === "UNAUTHENTICATED") location.assign("/login");
Correctts
if (isClientError(error, "UNAUTHENTICATED")) location.assign("/login");

Notes#

  • Origin. Generated clients create it from the error JSON { code, status, requestId, issues? }. An unknown 500 arrives as INTERNAL_ERROR, without details.
  • Observers. A ClientError is an expected response: useForm does not send it to the component's error observer, but does send any other failure.
  • Support. Display or log requestId: it is the same ID the server writes in its logs, so you can find the failure.

Was this page helpful?

Add a comment

It is sent when you choose “Yes” or “No”. Only the page, your answer and the comment are stored: no cookies and no personal data.

0 of 2000 characters

Edit on GitHub: docs/en/reference/utils/clienterror.md