You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Adds an "Uncaught exceptions" section that the dashboard's pretty-link
button now points at (#uncaught-exceptions). Covers what the error means,
the common EventEmitter-without-listener cause (with a node-redis example),
the .on("error", ...) fix, and the unhandledRejection path.
Copy file name to clipboardExpand all lines: docs/troubleshooting.mdx
+49Lines changed: 49 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -278,6 +278,55 @@ You could also offload the CPU-heavy work to a Node.js worker thread, but this i
278
278
279
279
If the above doesn't work, then we recommend you try increasing the machine size of your task. See our [machines guide](/machines) for more information.
280
280
281
+
### Uncaught exceptions
282
+
283
+
If you see a `TASK_RUN_UNCAUGHT_EXCEPTION` error, an exception escaped your task's `run()` function without being thrown through your `await` chain — the runtime caught it via Node's `process.on("uncaughtException")` handler. The dashboard surfaces this as a regular task failure (status `Failed`) and the run will retry according to your task's retry policy, but the exception still indicates a bug worth fixing.
284
+
285
+
The most common cause is a Node `EventEmitter` emitting an `"error"` event with no listener attached. When this happens, Node escalates the event into an `uncaughtException`. Long-lived clients like `node-redis`, `pg`, `kafkajs`, and `mongodb` all surface socket-level errors this way.
286
+
287
+
For example, a `node-redis` client with no error listener will fail your run with an `Error: read ECONNRESET` (or similar TCP error) the next time the socket is reset:
// GOOD: the listener catches socket-level errors. The awaited command
312
+
// (e.g. .get) will still reject if the connection is broken, and that
313
+
// rejection propagates through your run() and fails the attempt cleanly.
314
+
client.on("error", (err) => {
315
+
logger.warn("Redis client error", { err });
316
+
});
317
+
318
+
awaitclient.connect();
319
+
returnawaitclient.get("foo");
320
+
```
321
+
322
+
The same fix applies to any library that emits `"error"` events. As a rule, attach an `.on("error", ...)` listener to every long-lived client you create inside a task.
323
+
324
+
<Note>
325
+
326
+
Unhandled promise rejections (e.g. `Promise.reject(...)` with no `.catch`) take the same path — Node routes them through `uncaughtException` by default, and the runtime treats them as `TASK_RUN_UNCAUGHT_EXCEPTION` for the same reasons. Make sure every promise either gets `await`ed or has a `.catch(...)` handler.
Errors mentioning `sendBatchNonBlocking`, `@s2-dev/streamstore`, or `S2AppendSession` (often with `code: undefined`) can occur when you close a stream and then await `waitUntilComplete()`, or when a stream runs for a long time (e.g. 20+ minutes). Wrap `waitUntilComplete()` in try/catch so Transport/closed-stream errors don't fail your task:
0 commit comments