nylas / nylas/nylas-nodejs

Response body read errors bypass the SDK's typed error handling

Open
#757 0 comments 0 reactions 1 assignee View on GitHub

@radenkovic is already working on this.

Since Jul 28, 2026.

Dominant language
TypeScript
Stars
181
Forks
126
Avg merge
1d 20h
Merged PRs (30d)
1

Description

Describe the bug

When the HTTP connection drops while the SDK is reading a response body, the error that reaches my code is a raw TypeError: terminated from Node's fetch stack, not one of the SDK's own error types (NylasApiError, NylasOAuthError, NylasSdkTimeoutError). My catch block checks instanceof NylasApiError, so it falls through and I lose the request context I normally rely on (the requestId and flow id).

Looking at src/apiClient.ts, the two response-body reads are outside any try/catch:

  • requestWithResponse reads the body at line 317: const text = await response.text();. The try block starts on the next line and only wraps JSON.parse(text), so a failure in response.text() itself is not caught here.
  • requestRaw reads the body at line 348: const arrayBuffer = await response.arrayBuffer();, with no try/catch in the function.

This looks inconsistent with sendRequest, which already wraps fetch() and converts failures into SDK errors (for example AbortError becomes NylasSdkTimeoutError, and a non-2xx status becomes NylasApiError). A connection that fails before the first byte surfaces a typed error, but the same connection failing mid-body surfaces a raw undici error. The requestRaw path is the one I hit most, since it backs Attachments.download() and large downloads over a flaky network are the most likely to be cut off partway.

To Reproduce

Point the client at a local server that returns a success status and then closes the socket mid-body, so the body read rejects:

const http = require('http');
const Nylas = require('nylas').default;

// Server that sends headers + a partial body, then destroys the socket
const server = http.createServer((req, res) => {
  res.writeHead(200, { 'Content-Type': 'application/json' });
  res.write('{"request_id":"abc","data":');   // intentionally incomplete
  res.socket.destroy();                        // drop the connection mid-body
});

server.listen(0, async () => {
  const nylas = new Nylas({ apiKey: 'test', apiUri: `http://localhost:${server.address().port}` });

  try {
    await nylas.messages.list({ identifier: 'me' });
  } catch (e) {
    console.log('name:', e.name);                 // TypeError
    console.log('message:', e.message);           // terminated
    console.log('is NylasApiError:', e instanceof Nylas.NylasApiError); // false
  } finally {
    server.close();
  }
});

The same happens through nylas.attachments.download(...), which reads the body with response.arrayBuffer().

Expected behavior

A failure while reading the response body should be surfaced the same way as other transport failures: as an SDK error type, carrying the flow id / request id when available, so that a catch (e) { if (e instanceof NylasApiError) ... } block behaves consistently regardless of when in the request lifecycle the connection dropped. At minimum the raw error should be wrapped in a descriptive Error rather than leaking terminated.

SDK Version:

8.4.0

Additional context

The two spots are requestWithResponse (src/apiClient.ts:317) and requestRaw (src/apiClient.ts:348). Wrapping each body read in a try/catch and rethrowing an SDK-typed error, matching the pattern already used in sendRequest and in the existing JSON.parse catch, would keep the error surface consistent. Happy to open a PR with that change if it would be useful.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.