Features

Error Handling

Vextro exports a set of structured error classes from the vextro/errors entry point. These classes let you discriminate errors with instanceof in non-Convex code — auth middleware, Astro server actions, client-side hooks, and custom integrations.

Error hierarchy

All Vextro errors extend VextroError, which extends the native Error class and adds a code property:

import {
  VextroError,
  VextroPermissionError,
  VextroNotFoundError,
  VextroValidationError,
  VextroConflictError,
} from "vextro/errors";
ClasscodeWhen thrown
VextroPermissionErrorPERMISSION_DENIEDAuthentication or RBAC check failed
VextroNotFoundErrorNOT_FOUNDA document, collection, or resource was not found
VextroValidationErrorVALIDATION_ERRORInput fails schema or business-rule validation
VextroConflictErrorCONFLICTOptimistic concurrency conflict or duplicate detected

All error classes are also re-exported from the root vextro entry point.

Policy helpers and AuthorizationError

The createPolicyHelpers factory returns an AuthorizationError class that extends VextroPermissionError. All requireAuth, requireRole, and requirePermission calls throw AuthorizationError, which means any instanceof VextroPermissionError check also catches it:

import { createPolicyHelpers } from "vextro/convex/policy";
import { VextroPermissionError } from "vextro/errors";

const { requireRole, AuthorizationError } = createPolicyHelpers();

// In a Convex action (Node.js context):
try {
  await requireRole(ctx, "editor");
} catch (err) {
  if (err instanceof VextroPermissionError) {
    // catches both AuthorizationError and VextroPermissionError
  }
}

Convex server functions

Convex queries and mutations run in V8 isolates and serialize errors across the network. Plain Error instances (and subclasses) thrown in a query or mutation are not reconstructed as typed errors on the client — only ConvexError preserves structured data. Use ConvexError when you need to pass error payloads to the browser:

import { ConvexError } from "convex/values";

// Client receives: { code: "NOT_FOUND", message: "post not found: abc123" }
throw new ConvexError({ code: "NOT_FOUND", message: "post not found: abc123" });

Convex actions run in Node.js and can throw and catch VextroError subclasses within their own call stack before serializing a response. instanceof checks work normally inside an action.

Catching errors from Vextro integrations

The auth proxy (createAuthProxyHandler) always returns an HTTP response — it never throws. A network failure to the upstream Convex site returns 502 with { error: "Auth proxy unavailable" } so downstream code never needs to catch.

Extending VextroError

You can extend VextroError to add application-specific error types:

import { VextroError } from "vextro/errors";

export class RateLimitError extends VextroError {
  readonly retryAfter: number;

  constructor(retryAfter: number) {
    super("Rate limit exceeded", "RATE_LIMITED");
    this.name = "RateLimitError";
    this.retryAfter = retryAfter;
  }
}
Previous
Security