tursodatabase/turso · error · DatabaseError
SQL execution failed
Error message
SQL execution failed
What it means
DatabaseError thrown from Statement.iterate() when the cursor stream delivers a step_error or fatal error entry while rows are being yielded from the async generator. Unlike the buffered session path, the error surfaces mid-iteration — some rows may already have been consumed by your for-await loop before the throw; the literal text is the fallback when the entry has no error.message.
Source
Thrown at serverless/javascript/src/statement.ts:298
}
break;
case 'row':
if (entry.row) {
const decodedRow = entry.row.map(value => decodeValue(value, this.safeIntegerMode));
if (this.presentationMode === 'pluck') {
// In pluck mode, yield only the first column value
yield decodedRow[0];
} else if (this.presentationMode === 'raw') {
// In raw mode, yield arrays of values
yield decodedRow;
} else {
yield createExpandedRow(decodedRow, columns);
}
}
break;
case 'step_error':
case 'error':
throw new DatabaseError(entry.error?.message || 'SQL execution failed');
}
}
}
}
View on GitHub (pinned to bad083fafb)
Solutions
- Read the server message and DatabaseError.code to find the real cause; test the same SQL via all() to check whether it fails deterministically
- Handle partial results explicitly — iterate() may yield rows before throwing, so track what was processed
- For long queries, page through with LIMIT/keyset pagination instead of one giant stream
Example fix
// before
for await (const row of stmt.iterate()) {
await process(row); // error mid-stream aborts with rows already handled
}
// after
let handled = 0;
try {
for await (const row of stmt.iterate()) { await process(row); handled++; }
} catch (e) {
if (e instanceof Error && e.name === "DatabaseError") {
await resumeFromCheckpoint(handled); // explicit partial-result policy
} else throw e;
} Defensive patterns
Strategy: try-catch
Type guard
const isDatabaseError = (e: unknown): e is Error & { code?: string } =>
e instanceof Error && e.name === "DatabaseError"; Try / catch
let handled = 0;
try {
for await (const row of stmt.iterate(args)) {
await process(row);
handled++;
}
} catch (e) {
if (e instanceof Error && e.name === "DatabaseError") {
// `handled` rows were already processed — resume from a checkpoint,
// rerun idempotently, or surface a partial-result error
await resumeFromCheckpoint(handled);
} else throw e;
} Prevention
- Make per-row processing idempotent, or checkpoint progress, because iterate() can yield rows before failing
- Prefer LIMIT/keyset pagination for very large or long-running result sets
- Test iteration paths against server restarts so partial delivery is a handled case, not a crash
When it happens
Trigger: for await (const row of stmt.iterate(args)) where the query fails server-side partway through the stream (lock timeout, server restart, kill); message-less error entries producing the fallback wording; errors arriving after the first rows were already yielded and processed.
Common situations: Long-running scans interrupted by server restarts or timeouts; processing pipelines that assume all-or-nothing row delivery; iterating while other connections write conflicting data.
Related errors
- The supplied SQL string contains no statements
- reader is null
- Describe execution failed
- SQL execution failed
- Batch execution failed
AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16).
Data as JSON: /api/errors/60f07f1b08c8129c.
Report an issue: GitHub.