{"record":{"id":"abe499069c5e51cf","repo":"tursodatabase/turso","slug":"http-error-status-response-status","errorCode":null,"errorMessage":"HTTP error! status: ${response.status}","messagePattern":"HTTP error! status: (.+?)","errorType":"exception","errorClass":"DatabaseError","httpStatus":null,"severity":"error","filePath":"serverless/javascript/src/protocol.ts","lineNumber":317,"sourceCode":"  let response: Response;\n  try {\n    response = await fetch(`${ctx.url}/v3/cursor`, buildFetchOptions(ctx, JSON.stringify(request), signal));\n  } catch (error) {\n    wrapAbortError(error);\n  }\n\n  if (!response.ok) {\n    let errorMessage = `HTTP error! status: ${response.status}`;\n    try {\n      const errorBody = await response.text();\n      const errorData = JSON.parse(errorBody);\n      if (errorData.message) {\n        errorMessage = errorData.message;\n      }\n    } catch {\n      // If we can't parse the error body, use the default HTTP error message\n    }\n    throw new DatabaseError(errorMessage);\n  }\n\n  const reader = response.body?.getReader();\n  if (!reader) {\n    throw new DatabaseError('No response body');\n  }\n\n  const decoder = new TextDecoder();\n  let buffer = '';\n  let cursorResponse: CursorResponse | undefined;\n\n  // First, read until we get the cursor response (first line)\n  try {\n    while (!cursorResponse) {\n      const { done, value } = await reader.read();\n      if (done) break;\n\n      buffer += decoder.decode(value, { stream: true });","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/tursodatabase/turso/blob/c1e59287258d99b309e362a63f48822256e2f65f/serverless/javascript/src/protocol.ts#L299-L335","documentation":"The POST to <baseUrl>/v3/cursor returned a non-2xx status. This is the transport path used by execute(), executeRaw(), and batch(); the handler tries to read the response body as JSON and surface its message field, and only falls back to the literal 'HTTP error! status: N' when the body is not parseable JSON with a message. Authentication, URL, and request-shape problems all land here.","triggerScenarios":"401/403 from a missing, malformed, or expired authToken; 404 from a wrong URL or a deleted database; 400 from a stale baton after the server restarted the stream; an intermediary (proxy, gateway) returning an HTML error page so the body parse fails and you see only the status code.","commonSituations":"Database token expired in a long-running deployment; URL typo or pointing at libsql:// when https:// was expected (normalizeUrl handles libsql:// and turso:// but not arbitrary hosts); local `turso dev` not running; a corporate proxy intercepting the request.","solutions":["Verify credentials and endpoint: `turso db show <name> --url` and recreate the token if expired","Reproduce outside the app to see the raw body: curl -X POST <url>/v3/cursor -H 'Authorization: Bearer <token>' -d '{}'","Confirm the URL points at a Turso database endpoint (scheme libsql://, turso://, or https://) and the database still exists","Check whether a proxy/gateway sits in front of the database and inspect its error response"],"exampleFix":"// before\nconst session = new Session({ url: 'https://db.example.com', authToken: OLD_TOKEN });\nawait session.execute('SELECT 1'); // HTTP error! status: 401\n\n// after\nconst session = new Session({ url: process.env.TURSO_DATABASE_URL!, authToken: process.env.TURSO_AUTH_TOKEN! });\nawait session.execute('SELECT 1');","handlingStrategy":"try-catch","validationCode":"// fail fast on obviously bad config before first query\nfunction assertConfig(cfg: { url: string; authToken?: string }) {\n  if (!/^(https|libsql|turso):\\/\\//.test(cfg.url)) throw new Error(`bad url: ${cfg.url}`);\n  if (process.env.TURSO_AUTH_TOKEN === undefined) throw new Error('TURSO_AUTH_TOKEN not set');\n}","typeGuard":"import { DatabaseError } from '@tursodatabase/serverless';\n\nfunction isHttpError(e: unknown): e is DatabaseError {\n  return e instanceof DatabaseError && /^HTTP error! status: (\\d+)$/.test(e.message);\n}","tryCatchPattern":"try {\n  await session.execute(sql);\n} catch (e) {\n  if (isHttpError(e)) {\n    const status = Number(e.message.match(/status: (\\d+)/)?.[1]);\n    if (status === 401 || status === 403) await refreshToken(); // then rebuild Session and retry once\n  }\n  throw e;\n}","preventionTips":["Load url/authToken from environment at startup and validate presence, so failures surface at deploy not first query","Rotate tokens before expiry and recreate the Session afterwards — the token is captured per Session","Health-check with a cheap `SELECT 1` after building the Session to fail fast on 401/404","Note this path parses the server's JSON message; when you only see the status code, the body was not JSON — inspect it with curl"],"tags":["http","authentication","network","typescript"],"backgroundTag":"http-error-status","analyzedSha":"c1e59287258d99b309e362a63f48822256e2f65f","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}