Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,17 @@ This driver uses semantic versioning:
- A change in the major version (e.g. 1.Y.Z -> 2.0.0) indicates _breaking_
changes that require changes in your code to upgrade.

## [Unreleased]

### Fixed

- Fixed `ECONNRESET` errors in NextJS 15 production builds when using `next/cookies`
by explicitly setting the `Content-Length` header for all fixed-size request bodies.
The driver now calculates and sets `Content-Length` for strings (JSON and plain text),
Blobs, Buffers, FormData, and empty bodies, ensuring compatibility with NextJS 15
dynamic routes and other environments that don't automatically set this header.
([#831](https://github.com/arangodb/arangojs/issues/831))

## [10.2.0] - 2026-01-16

### Fixed
Expand Down
78 changes: 66 additions & 12 deletions src/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1166,21 +1166,75 @@ export class Connection {
// Workaround for ArangoDB 3.12.0-rc1 and earlier:
// Omitting the final CRLF results in "bad request body" fatal error
body = new Blob([blob, "\r\n"], { type: blob.type });
} else if (body) {
let contentType;
if (isBinary) {
contentType = "application/octet-stream";
} else if (typeof body === "object") {
body = JSON.stringify(body);
contentType = "application/json";
} else {
body = String(body);
contentType = "text/plain";
// Set content-length for Blob (needed for Next.js dynamic routes)
if (!headers.has("content-length")) {
headers.set("content-length", String(body.size));
}
if (!headers.has("content-type")) {
headers.set("content-type", contentType);
} else if (body !== null && body !== undefined) {
// Handle empty string explicitly
if (body === "") {
if (!headers.has("content-length")) {
headers.set("content-length", "0");
}
if (!headers.has("content-type")) {
headers.set("content-type", "text/plain");
}
} else {
let contentType;
if (isBinary) {
contentType = "application/octet-stream";
} else if (typeof body === "object") {
body = JSON.stringify(body);
contentType = "application/json";
} else {
body = String(body);
contentType = "text/plain";
}
if (!headers.has("content-type")) {
headers.set("content-type", contentType);
}
// Explicitly set content-length for fixed-size bodies
// This is needed for Next.js dynamic routes (e.g., when using cookies())
// and ensures compatibility with environments that don't set it automatically
if (!headers.has("content-length")) {
if (typeof body === "string") {
// Calculate byte length for UTF-8 encoded strings
// Buffer is available in Node.js (required: >=20) and browser polyfills
if (
typeof globalThis.Buffer !== "undefined" &&
globalThis.Buffer.byteLength
) {
const contentLength = globalThis.Buffer.byteLength(body, "utf8");
headers.set("content-length", String(contentLength));
} else {
// Fallback: use TextEncoder for browser environments
const encoder = new TextEncoder();
const contentLength = encoder.encode(body).length;
headers.set("content-length", String(contentLength));
}
} else if (body instanceof Blob) {
headers.set("content-length", String(body.size));
} else if (
typeof globalThis.Buffer !== "undefined" &&
globalThis.Buffer.isBuffer &&
globalThis.Buffer.isBuffer(body)
) {
headers.set("content-length", String(body.length));
}
// Note: ArangoDB does not support Transfer-Encoding: chunked.
// All public APIs only accept fixed-size bodies (string, Buffer, Blob, FormData, or objects that get JSON.stringify'd),
// so streams should never reach this point. If an unknown body type is passed, it will be converted to string above.
}
}
}
// Handle null/undefined body for POST/PUT/PATCH methods that might need content-length: 0
// This ensures compatibility with NextJS 15 and other environments that expect explicit content-length
else if (
(method === "POST" || method === "PUT" || method === "PATCH") &&
!headers.has("content-length")
) {
headers.set("content-length", "0");
}

if (this._transactionId) {
headers.set("x-arango-trx-id", this._transactionId);
Expand Down
4 changes: 3 additions & 1 deletion src/test/32-access-tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ describe312("Access Tokens", function () {
});

after(async () => {
const cleanupSystem = new Database(config);
try {
await system.removeUser(testUsername);
await cleanupSystem .removeUser(testUsername);
} catch (err) {
// User might already be deleted, ignore
} finally {
try {
cleanupSystem .close();
system.close();
} catch (err) {
// Connection may already be closed, ignore
Expand Down
Loading