Describe the bug
When a bearer access method is configured with DURATION FOR SESSION exceeding ~24.8 days (2^31 - 1 ms), the JS SDK sets a setTimeout internally for session expiry. Node.js overflows the 32-bit signed integer used for setTimeout and clamps it to 1ms:
TimeoutOverflowWarning: 7775940000 does not fit into a 32-bit signed integer.
Timeout duration was set to 1.
The session is then silently expired after 1ms. Any await between two db.query() calls yields to the Node.js event loop, the 1ms timer fires, the session is invalidated, and the next query runs unauthenticated:
NotAllowedError: Anonymous access not allowed: Not enough permissions to perform this action
This is entirely invisible — no error is thrown at signin time, and the first query (which runs synchronously before any yield) succeeds normally.
Steps to reproduce
- Define a bearer access method with session duration > 24.8 days:
DEFINE ACCESS token_access ON DATABASE TYPE BEARER
FOR RECORD
DURATION FOR GRANT 90d, FOR TOKEN 90d, FOR SESSION 90d;
- Sign in with a bearer key over WebSocket:
const db = new Surreal();
await db.connect('ws://localhost:8000', { namespace: 'main', database: 'main' });
await db.signin({ namespace: 'main', database: 'main', access: 'token_access', key: bearerKey });
- Run two queries with any
await between them:
// Query 1 — succeeds
const [auth1] = await db.query('RETURN $auth');
console.log(auth1); // "user:abc123"
// Any await yields to the event loop — the 1ms timer fires here
await Promise.resolve();
// Query 2 — fails with NotAllowedError
const [auth2] = await db.query('RETURN $auth');
// NotAllowedError: Anonymous access not allowed
Expected behaviour
$auth should be preserved for the lifetime of the configured session duration. A 90-day session should not expire after 1ms.
Minimal reproducer
import { Surreal } from 'surrealdb';
const db = new Surreal();
await db.connect('ws://localhost:8000', { namespace: 'main', database: 'main' });
await db.signin({ namespace: 'main', database: 'main', access: 'token_access', key: 'surreal-bearer-...' });
const [a1] = await db.query('RETURN $auth');
console.log('Before yield:', a1); // user:abc123
await Promise.resolve(); // ← single microtask yield — triggers the expired timer
const [a2] = await db.query('RETURN $auth');
console.log('After yield:', a2);
// NotAllowedError: Anonymous access not allowed ($auth is now NONE)
Node.js prints the warning at signin time:
TimeoutOverflowWarning: 7775940000 does not fit into a 32-bit signed integer.
Timeout duration was set to 1.
7,775,940,000 ms = 90 days.
Three-scenario comparison
| Scenario |
Between queries |
Result |
| A |
Nothing (sync) |
✓ PRESERVED |
| B |
External new Surreal() open/close |
✗ LOST |
| C |
setTimeout(resolve, 100) |
✗ LOST |
| D |
await Promise.resolve() |
✗ LOST |
Scenario D proves it is not the second connection that causes the loss — any microtask yield is sufficient.
Root cause hypothesis
The SDK likely computes the session expiry duration in milliseconds and passes it directly to setTimeout(). For sessions > ~24.8 days the value exceeds 2^31 - 1, which Node.js clamps to 1ms. The fix would be to cap the timer at 2^31 - 1 ms (and reschedule if needed), or to avoid relying on a JS timer for session invalidation entirely.
SurrealDB version
surrealdb/surrealdb:v3.0.5
SDK version
surrealdb@2.0.3
Node.js version
v24 (also reproduced on v22)
Connection type
WebSocket (ws://)
Code of Conduct
Describe the bug
When a bearer access method is configured with
DURATION FOR SESSIONexceeding ~24.8 days (2^31 - 1 ms), the JS SDK sets asetTimeoutinternally for session expiry. Node.js overflows the 32-bit signed integer used forsetTimeoutand clamps it to 1ms:The session is then silently expired after 1ms. Any
awaitbetween twodb.query()calls yields to the Node.js event loop, the 1ms timer fires, the session is invalidated, and the next query runs unauthenticated:This is entirely invisible — no error is thrown at signin time, and the first query (which runs synchronously before any yield) succeeds normally.
Steps to reproduce
awaitbetween them:Expected behaviour
$authshould be preserved for the lifetime of the configured session duration. A 90-day session should not expire after 1ms.Minimal reproducer
Node.js prints the warning at signin time:
7,775,940,000 ms = 90 days.
Three-scenario comparison
new Surreal()open/closesetTimeout(resolve, 100)await Promise.resolve()Scenario D proves it is not the second connection that causes the loss — any microtask yield is sufficient.
Root cause hypothesis
The SDK likely computes the session expiry duration in milliseconds and passes it directly to
setTimeout(). For sessions > ~24.8 days the value exceeds2^31 - 1, which Node.js clamps to 1ms. The fix would be to cap the timer at2^31 - 1ms (and reschedule if needed), or to avoid relying on a JS timer for session invalidation entirely.SurrealDB version
surrealdb/surrealdb:v3.0.5SDK version
surrealdb@2.0.3Node.js version
v24 (also reproduced on v22)
Connection type
WebSocket (
ws://)Code of Conduct