From d049151b76db499b7f9a4e9e48e25351314ef425 Mon Sep 17 00:00:00 2001 From: Yuchen Zhang Date: Fri, 31 Jul 2026 12:47:35 -0700 Subject: [PATCH 1/2] docs: clarify middleware callback error semantics Signed-off-by: Yuchen Zhang --- docs/about-nemo-relay/concepts/middleware.mdx | 11 +++++++++++ docs/reference/migration-guides.mdx | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/docs/about-nemo-relay/concepts/middleware.mdx b/docs/about-nemo-relay/concepts/middleware.mdx index 2a3aabe94..a8ce3f2d1 100644 --- a/docs/about-nemo-relay/concepts/middleware.mdx +++ b/docs/about-nemo-relay/concepts/middleware.mdx @@ -104,6 +104,17 @@ NeMo Relay has two major middleware families: - **Intercepts** change the real execution path - **Guardrails** block work or rewrite emitted observability payloads +### Callback Error Handling + +Conditional-execution guardrails and request or execution intercepts fail +closed. If one of these callbacks returns an error, raises an exception, or +rejects a `Promise`, Relay stops at that middleware stage and surfaces the error +to the managed caller. A conditional guardrail or request intercept therefore +prevents the real callback from running when it fails. An execution intercept +cannot undo work from a `next` continuation that it already invoked. This +fail-closed contract is the same whether the callback completes directly or +asynchronously. + ## Intercepts Intercepts are middleware that change the real request or execution path. diff --git a/docs/reference/migration-guides.mdx b/docs/reference/migration-guides.mdx index 91d95bdf2..d5fbae138 100644 --- a/docs/reference/migration-guides.mdx +++ b/docs/reference/migration-guides.mdx @@ -46,6 +46,14 @@ existing error behavior for its middleware family. | Node.js | Direct return value | Direct return value or `Promise` | | Go / raw C FFI | Synchronous callback | Synchronous callback | +Conditional-execution guardrails and request or execution intercepts retain a +fail-closed exception contract in 0.7. Relay stops at the failing middleware +stage and surfaces the error to the managed caller. Conditional guardrail and +request-intercept failures prevent the real callback from running. This +behavior is the same for callbacks that return directly and callbacks that +complete asynchronously. An execution intercept cannot undo work from a `next` +continuation that it already invoked before failing. + The experimental Go and raw C FFI callbacks remain synchronous. Relay waits for each callback on a native thread, so blocking I/O and other long-running callback work occupy that thread. No completion-based registration API is From 57780bfdc1067e89abdce4dd5b0c171ff732bde3 Mon Sep 17 00:00:00 2001 From: Yuchen Zhang Date: Fri, 31 Jul 2026 14:14:20 -0700 Subject: [PATCH 2/2] docs: address middleware review feedback Signed-off-by: Yuchen Zhang --- docs/about-nemo-relay/concepts/middleware.mdx | 17 ++++++++++------- docs/reference/migration-guides.mdx | 5 +++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/about-nemo-relay/concepts/middleware.mdx b/docs/about-nemo-relay/concepts/middleware.mdx index a8ce3f2d1..e544671a0 100644 --- a/docs/about-nemo-relay/concepts/middleware.mdx +++ b/docs/about-nemo-relay/concepts/middleware.mdx @@ -107,13 +107,16 @@ NeMo Relay has two major middleware families: ### Callback Error Handling Conditional-execution guardrails and request or execution intercepts fail -closed. If one of these callbacks returns an error, raises an exception, or -rejects a `Promise`, Relay stops at that middleware stage and surfaces the error -to the managed caller. A conditional guardrail or request intercept therefore -prevents the real callback from running when it fails. An execution intercept -cannot undo work from a `next` continuation that it already invoked. This -fail-closed contract is the same whether the callback completes directly or -asynchronously. +closed when their callbacks return a failure result. A failure returned +directly or produced when a callback raises an exception or rejects a `Promise` +stops Relay at that middleware stage, and Relay surfaces the failure to the +managed caller. A conditional guardrail or request intercept therefore prevents +the real callback from running when it fails. Separately, a +conditional-execution guardrail can return its documented rejection message to +block execution. A rejection is normal control flow, not a callback failure. An +execution intercept cannot undo work from a `next` continuation that it already +invoked. This fail-closed contract is the same whether the callback completes +directly or asynchronously. ## Intercepts diff --git a/docs/reference/migration-guides.mdx b/docs/reference/migration-guides.mdx index d5fbae138..d45294c17 100644 --- a/docs/reference/migration-guides.mdx +++ b/docs/reference/migration-guides.mdx @@ -51,8 +51,9 @@ fail-closed exception contract in 0.7. Relay stops at the failing middleware stage and surfaces the error to the managed caller. Conditional guardrail and request-intercept failures prevent the real callback from running. This behavior is the same for callbacks that return directly and callbacks that -complete asynchronously. An execution intercept cannot undo work from a `next` -continuation that it already invoked before failing. +complete asynchronously. If an execution intercept fails after invoking `next`, +Relay cannot undo completed downstream work. Relay rejects `next` calls that +remain unfinished and calls that begin after the interceptor settles. The experimental Go and raw C FFI callbacks remain synchronous. Relay waits for each callback on a native thread, so blocking I/O and other long-running