{"record":{"id":"f6d33dad7461fa10","repo":"tursodatabase/turso","slug":"query-timed-out","errorCode":null,"errorMessage":"Query timed out","messagePattern":"Query timed out","errorType":"exception","errorClass":"TimeoutError","httpStatus":null,"severity":"error","filePath":"serverless/javascript/src/protocol.ts","lineNumber":289,"sourceCode":"  };\n}\n\n/** Per-query options. Override the session-level defaults for a single call. */\nexport interface QueryOptions {\n  /** Per-query timeout in milliseconds. Overrides defaultQueryTimeout for this call. */\n  queryTimeout?: number;\n  /**\n   * Extra HTTP headers attached to this request only. Applied after the\n   * standard headers and any session-level `requestHeaders`, so they can\n   * override both. Passing the `Host` key (case-insensitive) throws —\n   * fetch forbids setting it.\n   */\n  requestHeaders?: Record<string, string>;\n}\n\nfunction wrapAbortError(error: unknown): never {\n  if (error instanceof Error && (error.name === 'AbortError' || error.name === 'TimeoutError')) {\n    throw new TimeoutError('Query timed out');\n  }\n  throw error;\n}\n\nexport async function executeCursor(\n  ctx: HttpContext,\n  request: CursorRequest,\n  signal?: AbortSignal\n): Promise<{ response: CursorResponse; entries: AsyncGenerator<CursorEntry> }> {\n  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}`;","sourceCodeStart":271,"sourceCodeEnd":307,"githubUrl":"https://github.com/tursodatabase/turso/blob/c1e59287258d99b309e362a63f48822256e2f65f/serverless/javascript/src/protocol.ts#L271-L307","documentation":"Thrown when a query exceeds its deadline. Session.createAbortSignal builds an AbortSignal.timeout(ms) from queryOptions.queryTimeout or config.defaultQueryTimeout, and wrapAbortError converts the resulting fetch AbortError/TimeoutError into the library's TimeoutError (a DatabaseError subclass with code === 'TIMEOUT'). It wraps both the initial fetch and every reader.read() on the response stream, so a query can also time out mid-stream while rows are still arriving.","triggerScenarios":"Any session.execute/executeRaw/batch/describe/sequence call (or Statement.run/get/all/iterate built on them) that runs longer than defaultQueryTimeout, or than the per-call queryTimeout passed in queryOptions. Also fires when the timeout aborts during streaming of result rows, not just at request start.","commonSituations":"defaultQueryTimeout copied from sample code and set too low for a large batch insert; slow unindexed SELECT over a big table; serverless cold-start latency eating the budget; slow network between the edge function and the database.","solutions":["Pass a larger per-call timeout: execute(sql, args, false, { queryTimeout: 60000 })","Raise or remove the session-level default: new Session({ url, authToken, defaultQueryTimeout: 0 }) (0 or omitted disables the deadline)","Speed the query up: add an index, add LIMIT, or split one huge batch into smaller ones","Catch TimeoutError specifically (code === 'TIMEOUT') and retry idempotent read-only queries"],"exampleFix":"// before\nconst session = new Session({ url, authToken, defaultQueryTimeout: 1000 });\nawait session.execute('SELECT count(*) FROM huge_table'); // TimeoutError after 1s\n\n// after\nconst session = new Session({ url, authToken, defaultQueryTimeout: 30000 });\n// or per call:\nawait session.execute('SELECT count(*) FROM huge_table', [], false, { queryTimeout: 30000 });","handlingStrategy":"retry","validationCode":"// sanity-check timeout config before starting work\nconst timeout = queryOptions?.queryTimeout ?? config.defaultQueryTimeout;\nif (timeout != null && timeout !== 0 && timeout < estimatedWorstCaseMs) {\n  throw new Error(`queryTimeout ${timeout}ms is below the estimated worst case`);\n}","typeGuard":"import { DatabaseError } from '@tursodatabase/serverless';\n\nfunction isQueryTimeout(e: unknown): e is DatabaseError {\n  return e instanceof DatabaseError && e.code === 'TIMEOUT';\n}","tryCatchPattern":"try {\n  return await session.execute(sql, args);\n} catch (e) {\n  if (isQueryTimeout(e) && attempt < MAX_RETRIES && isIdempotent(sql)) {\n    await backoff(attempt++);\n    return retry();\n  }\n  throw e;\n}","preventionTips":["Measure real query latency and set defaultQueryTimeout with headroom (p99 x 2-3), not a guess","Pass per-call queryTimeout for known-slow maintenance queries instead of raising the global default","Keep queries idempotent (or mark them) so timeout retries are safe","Remember the deadline also covers row streaming — large result sets need proportionally larger timeouts or iterate()"],"tags":["timeout","network","typescript","serverless"],"backgroundTag":"query-timeout","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"}