tursodatabase/turso · error · DatabaseError
overwriting the 'Host' header is not supported
Error message
overwriting the 'Host' header is not supported
What it means
The Session constructor scans config.requestHeaders and throws when any key equals 'host' case-insensitively. Host is a forbidden fetch header — fetch would silently drop the override — so the library rejects it up front to make the failure loud instead of silent. The same check runs per-request in buildHeaders (protocol.ts:242) for headers that only arrive via QueryOptions.requestHeaders.
Solutions
- Remove the Host entry from requestHeaders — route by choosing the correct url instead
- If you need host-based routing, put the target host in the URL and let fetch set Host itself
- If the header object is dynamic, filter it before constructing the Session
Example fix
// before
const session = new Session({
url: 'https://db.example.com',
authToken,
requestHeaders: { Host: 'db.internal.example.com' }, // throws
});
// after
const session = new Session({
url: 'https://db.internal.example.com',
authToken,
requestHeaders: { 'x-custom-trace-id': traceId },
}); Defensive patterns
Strategy: validation
Validate before calling
// strip forbidden headers before constructing the Session
function sanitizeHeaders(headers: Record<string, string> = {}): Record<string, string> {
const out: Record<string, string> = {};
for (const [k, v] of Object.entries(headers)) {
if (k.toLowerCase() === 'host') continue; // fetch forbids setting Host
out[k] = v;
}
return out;
} Try / catch
try {
new Session(config);
} catch (e) {
if (e instanceof DatabaseError && e.message.includes("'Host' header")) {
delete config.requestHeaders?.Host;
// retry construction
}
} Prevention
- Route by URL, never by Host header — fetch silently drops forbidden headers so the library rejects them loudly
- Filter dynamic header objects through a deny-list before passing them to Session config or queryOptions
- Remember per-query requestHeaders containing Host throw at request time (protocol.ts buildHeaders), not at construction
When it happens
Trigger: Constructing a Session (or the client that builds one) with requestHeaders: { Host: '...' } or { host: '...' }; passing per-query requestHeaders containing Host via queryOptions, which skips the constructor check and throws at request time with the same message.
Common situations: Porting code that set Host for virtual-host routing with another HTTP client; copy-pasted header objects from curl -H 'Host: ...' recipes; multi-tenant routing attempts through a shared gateway.
Related errors
- Expected first argument to be an array of statements
- overwriting the 'Host' header is not supported
- retryFetch: attempts must be a finite integer >= 1, got
- Batch command text must contain a SQL statement.
- Batch execution failed
AI-assisted analysis of tursodatabase/turso@c1e5928725 (2026-08-20).
Data as JSON: /api/errors/9e02b04f012b8c2a.
Report an issue: GitHub.
Appendix: source
Thrown at serverless/javascript/src/session.ts:141
* A database session that manages the connection state and baton.
*
* Each session maintains its own connection state and can execute SQL statements
* independently without interfering with other sessions.
*/
export class Session {
private config: SessionConfig;
private baton: string | null = null;
private baseUrl: string;
// Cached autocommit status from the server's last `get_autocommit` answer.
// A fresh connection is in autocommit (not in a transaction).
private autocommit: boolean = true;
constructor(config: SessionConfig) {
for (const name of Object.keys(config.requestHeaders ?? {})) {
// `Host` is a forbidden fetch header and would be silently dropped —
// reject it up front so the caller learns the override never takes effect.
if (name.toLowerCase() === 'host') {
throw new DatabaseError("overwriting the 'Host' header is not supported");
}
}
this.config = config;
this.baseUrl = normalizeUrl(config.url);
}
private httpContext(queryOptions?: QueryOptions): HttpContext {
// Per-query headers are merged over the session-level ones, so a query
// can override a header the session sets (and both override the
// standard headers).
let requestHeaders = this.config.requestHeaders;
if (queryOptions?.requestHeaders) {
requestHeaders = { ...requestHeaders, ...queryOptions.requestHeaders };
}
return {
url: this.baseUrl,
authToken: this.config.authToken,
remoteEncryptionKey: this.config.remoteEncryptionKey,View on GitHub (pinned to c1e5928725)