From 17f9d09d1939447025de1998dd154197a56953fa Mon Sep 17 00:00:00 2001 From: puckey Date: Mon, 1 Dec 2025 14:33:30 +0100 Subject: [PATCH] feat: Improve error message for cyclic struct references Add helpful error message when stack overflow occurs due to cyclic struct references, with examples of both direct and indirect cycles and a link to the docs. Also document this limitation in custom-structs.md. --- docs/docs/types/custom-structs.md | 19 +++++++++++++++++++ packages/nitrogen/src/utils.ts | 8 ++++++++ 2 files changed, 27 insertions(+) diff --git a/docs/docs/types/custom-structs.md b/docs/docs/types/custom-structs.md index 3cc2f2110..130849fe2 100644 --- a/docs/docs/types/custom-structs.md +++ b/docs/docs/types/custom-structs.md @@ -103,6 +103,25 @@ interface PartialPerson This way TypeScript always keeps the `interface` in-tact, allowing Nitrogen to properly process it. +## Cyclic references are not supported + +Direct or indirect cyclic struct references are not supported: + +```ts title="Direct cycle ❌" +interface Node { + child: Node +} +``` + +```ts title="Indirect cycle ❌" +interface A { + b: B +} +interface B { + a: A +} +``` + ## Structs are eagerly converted Since structs are just flat value types, each key/value is eagerly converted from a JS value to a native value (and vice-versa) when passing them between JS and native. diff --git a/packages/nitrogen/src/utils.ts b/packages/nitrogen/src/utils.ts index d841be374..b7199155c 100644 --- a/packages/nitrogen/src/utils.ts +++ b/packages/nitrogen/src/utils.ts @@ -35,6 +35,14 @@ export function errorToString(error: unknown): string { if (typeof error !== 'object') { return `${error}` } + if (error instanceof RangeError && error.message.includes('Maximum call stack size exceeded')) { + return ( + `${error.name}: ${error.message}\n` + + `This is likely caused by a direct or indirect cyclic struct reference ` + + `(e.g. \`interface Node { child: Node }\` or \`interface A { b: B }\` and \`interface B { a: A }\`).\n` + + `See: https://nitro.margelo.com/docs/types/custom-structs#cyclic-references-are-not-supported` + ) + } if (error instanceof Error) { let message = `${error.name}: ${error.message}` if (error.cause != null) {