{"record":{"id":"7f3577d47e5334d0","repo":"usebruno/bruno","slug":"status-statuscode","errorCode":null,"errorMessage":"Status ${statusCode}","messagePattern":"Status (.+?)","errorType":"exception","errorClass":"VaultError","httpStatus":null,"severity":"error","filePath":"packages/bruno-requests/src/utils/node-vault.ts","lineNumber":128,"sourceCode":"  // Success responses\n  if (statusCode === 200 || statusCode === 204) {\n    return body;\n  }\n\n  // Health endpoint special handling (matches node-vault behavior)\n  if (path.match(/sys\\/health/) !== null) {\n    return body;\n  }\n\n  // Error responses\n  let message: string;\n  if (body && body.errors && body.errors.length > 0) {\n    message = body.errors[0];\n  } else {\n    message = `Status ${statusCode}`;\n  }\n\n  throw new VaultError(message, { statusCode, body });\n}\n\n/**\n * Creates a Vault client instance\n *\n * This is a drop-in replacement for node-vault, implementing only the methods\n * used by bruno-electron and bruno-cli.\n *\n * @param config - Configuration options\n * @returns VaultClient instance with mutable properties\n *\n * @example\n * ```javascript\n * const vault = createVaultClient({ apiVersion: 'v1' });\n * vault.endpoint = 'https://vault.example.com';\n * vault.token = 'my-token';\n * const secret = await vault.read('secret/data/myapp');\n * ```","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/usebruno/bruno/blob/9bdd81c7bdc57006e5f5ebffb79321a8d979f712/packages/bruno-requests/src/utils/node-vault.ts#L110-L146","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Catch VaultError and read err.details.statusCode and err.details.body to recover the real cause before falling back to the generic message.","For 403: re-authenticate the token (vault.token = newToken) and retry once.","For 404: verify the path and apiVersion (e.g. secret/data/... for kv-v2 vs secret/... for kv-v1).","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.","Enable the client debug callback (config.debug) to log statusCode/body for unresolved cases."],"exampleFix":"// before\nconst secret = await vault.read('secret/data/myapp');\n\n// after\ntry {\n  const secret = await vault.read('secret/data/myapp');\n} catch (e) {\n  if (e?.constructor?.name === 'VaultError' && e.details?.statusCode === 403) {\n    vault.token = await refreshToken();\n    return vault.read('secret/data/myapp');\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":"// Before bulk reads, cheaply verify reachability and auth shape.\nasync function vaultOk(vault) {\n  try {\n    const health = await vault.read('sys/health'); // health endpoint bypasses the generic throw\n    return !health?.sealed && !health?.standby;\n  } catch {\n    return false;\n  }\n}\nif (!(await vaultOk(vault))) {\n  throw new Error('Vault unreachable/sealed before request');\n}","typeGuard":"function isVaultError(e) {\n  return e instanceof Error && e.constructor && e.constructor.name === 'VaultError'\n    && Object.prototype.hasOwnProperty.call(e, 'details');\n}\n// or, if VaultError is exported: return e instanceof VaultError;","tryCatchPattern":"try {\n  return await vault.read(path);\n} catch (e) {\n  if (!isVaultError(e)) throw e;\n  const { statusCode, body } = e.details ?? {};\n  if (statusCode === 403) { vault.token = await refreshToken(); return await vault.read(path); }\n  if (statusCode === 404) return null; // treat missing secret as absent\n  if (statusCode >= 500) throw new Error(`Vault upstream error ${statusCode}: ${safeBody(body)}`);\n  throw e;\n}","preventionTips":["Always read err.details.statusCode/body rather than trusting the generic 'Status N' message.","Refresh tokens proactively before expiry instead of relying on 403 recovery.","Pin apiVersion and path layout (kv-v2 uses secret/data/...) to avoid 404 false-negatives.","Health-check sys/health at startup to fail fast on sealed/unreachable Vault."],"tags":["vault","secrets","api-error","authentication","network"],"backgroundTag":null,"analyzedSha":"9bdd81c7bdc57006e5f5ebffb79321a8d979f712","analyzedAt":"2026-08-13T04:09:25.751Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}