usebruno/bruno · error · VaultError

Status ${statusCode}

Error message

Status ${statusCode}

What it means

Thrown by handleVaultResponse when the Vault server returns a status other than 200/204 on a non-/sys/health path AND the response body has no usable errors[] array. It is the fallback branch of the node-vault drop-in: when Vault (or something in front of it) gives an error shape the client does not recognize, the client cannot extract a real reason and reports only the raw HTTP status. The thrown value is a VaultError carrying { statusCode, body } so callers can still inspect both.

Source

Thrown at packages/bruno-requests/src/utils/node-vault.ts:128

  // Success responses
  if (statusCode === 200 || statusCode === 204) {
    return body;
  }

  // Health endpoint special handling (matches node-vault behavior)
  if (path.match(/sys\/health/) !== null) {
    return body;
  }

  // Error responses
  let message: string;
  if (body && body.errors && body.errors.length > 0) {
    message = body.errors[0];
  } else {
    message = `Status ${statusCode}`;
  }

  throw new VaultError(message, { statusCode, body });
}

/**
 * Creates a Vault client instance
 *
 * This is a drop-in replacement for node-vault, implementing only the methods
 * used by bruno-electron and bruno-cli.
 *
 * @param config - Configuration options
 * @returns VaultClient instance with mutable properties
 *
 * @example
 * ```javascript
 * const vault = createVaultClient({ apiVersion: 'v1' });
 * vault.endpoint = 'https://vault.example.com';
 * vault.token = 'my-token';
 * const secret = await vault.read('secret/data/myapp');
 * ```

View on GitHub (pinned to 9bdd81c7bd)

Solutions

  1. Catch VaultError and read err.details.statusCode and err.details.body to recover the real cause before falling back to the generic message.
  2. For 403: re-authenticate the token (vault.token = newToken) and retry once.
  3. For 404: verify the path and apiVersion (e.g. secret/data/... for kv-v2 vs secret/... for kv-v1).
  4. For 5xx with an HTML/plain body: inspect whether an intermediate proxy or Vault's listener is misconfigured; check Vault seal status and upstream health.
  5. Enable the client debug callback (config.debug) to log statusCode/body for unresolved cases.

Example fix

// before
const secret = await vault.read('secret/data/myapp');

// after
try {
  const secret = await vault.read('secret/data/myapp');
} catch (e) {
  if (e?.constructor?.name === 'VaultError' && e.details?.statusCode === 403) {
    vault.token = await refreshToken();
    return vault.read('secret/data/myapp');
  }
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Before bulk reads, cheaply verify reachability and auth shape.
async function vaultOk(vault) {
  try {
    const health = await vault.read('sys/health'); // health endpoint bypasses the generic throw
    return !health?.sealed && !health?.standby;
  } catch {
    return false;
  }
}
if (!(await vaultOk(vault))) {
  throw new Error('Vault unreachable/sealed before request');
}

Type guard

function isVaultError(e) {
  return e instanceof Error && e.constructor && e.constructor.name === 'VaultError'
    && Object.prototype.hasOwnProperty.call(e, 'details');
}
// or, if VaultError is exported: return e instanceof VaultError;

Try / catch

try {
  return await vault.read(path);
} catch (e) {
  if (!isVaultError(e)) throw e;
  const { statusCode, body } = e.details ?? {};
  if (statusCode === 403) { vault.token = await refreshToken(); return await vault.read(path); }
  if (statusCode === 404) return null; // treat missing secret as absent
  if (statusCode >= 500) throw new Error(`Vault upstream error ${statusCode}: ${safeBody(body)}`);
  throw e;
}

Prevention

When it happens

Trigger: Any Vault read/write/list/delete returns 3xx/4xx/5xx with a body that is not JSON, is empty, or lacks a top-level errors array. Examples: 403 with a plain {'permission_denied': true} shape; 404 with empty body on a wrong path; 502/504 HTML from a reverse proxy or load balancer sitting in front of Vault; 429 with a rate-limit body not using the errors convention; 473/503 while Vault is sealed or standby.

Common situations: Token expired or revoked (403) but Vault returns a non-standard body; wrong mount path (404); Vault sealed or in standby behind a VIP; a corporate proxy/gateway intercepting the request and returning an HTML error page; self-signed cert rejected by an upstream that then returns a generic 502.

Related errors


AI-assisted analysis of usebruno/bruno@9bdd81c7bd (2026-08-13). Data as JSON: /api/errors/7f3577d47e5334d0. Report an issue: GitHub.