VeilBrowserDocs

Errors

The response envelope, top-level error codes, and field-level validation details.

Every VeilBrowser API returns the same envelope, so a single client-side handler covers both the cloud API and workers.

Response envelope

Successful responses carry the payload under data:

200 OK
{
  "success": true,
  "data": { "id": "prof_01j...", "name": "marketing-us" }
}

Failures carry a structured error instead:

404 Not Found
{
  "success": false,
  "error": {
    "code": "RESOURCE_NOT_FOUND",
    "message": "Profile not found: prof_01j..."
  }
}

success is a literal discriminant, so narrowing on it is safe in TypeScript.

Error codes

error.code is one of eight values. Branch on this, not on the HTTP status or the message text.

Prop

Type

One code, several statuses

A few codes map to more than one HTTP status because the same class of problem can surface at different layers. RESOURCE_NOT_FOUND is 404 for a missing profile but 400 when a launch requests a browser version the worker does not have installed — the request itself is malformed in that case.

Validation details

VALIDATION_ERROR responses include a details array with one entry per offending field, so you can map failures straight onto a form.

400 Bad Request
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request validation failed",
    "details": [
      {
        "field": "browserVersion",
        "code": "TOO_SMALL",
        "message": "Browser version must be at least 135",
        "received": 131,
        "constraint": { "min": 135, "max": 150 }
      }
    ]
  }
}

Prop

Type

Field-level code values: INVALID_TYPE, INVALID_FORMAT, TOO_SHORT, TOO_LONG, INVALID_PATTERN, TOO_SMALL, TOO_BIG, NOT_INTEGER, ARRAY_TOO_SHORT, ARRAY_TOO_LONG, INVALID_ENUM_VALUE, INVALID_UNION, REQUIRED, UNRECOGNIZED_KEY, CUSTOM.

Some errors enrich constraint with actionable data rather than plain bounds. A launch against an uninstalled browser version returns the versions that host does have:

{
  "field": "browserVersion",
  "code": "RESOURCE_NOT_FOUND",
  "message": "Browser version 149 not found",
  "received": 149,
  "constraint": { "availableVersions": [135, 141, 147] }
}

Handling errors

client.ts
type ApiError = {
  success: false;
  error: { code: string; message: string; details?: { field: string; message: string }[] };
};

const res = await fetch(`${base}/v1/profiles`, { method: "POST", body });
const body = await res.json();

if (!body.success) {
  const { code, message, details } = (body as ApiError).error;

  switch (code) {
    case "VALIDATION_ERROR":
      // Map details onto form fields
      for (const d of details ?? []) markInvalid(d.field, d.message);
      break;
    case "AUTHORIZATION_ERROR":
      // Plan limit or missing role — surface, do not retry
      throw new Error(message);
    case "EXTERNAL_SERVICE_ERROR":
    case "INTERNAL_ERROR":
      // Transient — safe to retry with backoff
      return retry();
    default:
      throw new Error(message);
  }
}

Retry EXTERNAL_SERVICE_ERROR and INTERNAL_ERROR with exponential backoff. Never retry VALIDATION_ERROR, AUTHENTICATION_ERROR or AUTHORIZATION_ERROR — the same request will fail identically.

Launch authorization failures

License authorization carries a different payload but the same envelope. POST /v1/license/authorize returns 402 Payment Required with an explicit reason when a launch is refused, wrapped in the standard { success, data } shape:

402 Payment Required
{
  "success": true,
  "data": {
    "allowed": false,
    "reason": "concurrency_limit",
    "current": 200,
    "limit": 200
  }
}

success reports that the authorization check itself completed, not that the launch was permitted. Read data.allowed for the verdict. A refusal the check never reached, such as a bad API key, comes back as an ordinary { success: false, error } body.

Workers fail closed on this response, and on any failure to reach the cloud. See Licensing & concurrency and Troubleshooting.

Next steps

  • Limits — the plan ceilings behind AUTHORIZATION_ERROR.
  • API reference — the endpoints these codes come back from.
Was this page helpful?

On this page