Skip to content

Proxy 502 errors omit the underlying fetch error (no cause on H3Error)Β #756

@felixgabler

Description

@felixgabler

πŸ› The bug

When the upstream fetch() in proxy-handler.ts rejects β€” timeout, ECONNRESET, DNS, TLS error, etc. β€” the catch block creates a 502 H3Error but doesn't attach the original error:

// dist/runtime/server/proxy-handler.js (v1.0.6, lines 252–258)
} catch (err) {
  log("[proxy] Upstream error:", err);
  throw createError({
    statusCode: 502,
    statusMessage: "Bad Gateway",
    message: `Proxy upstream request failed: ${targetUrl}`
  });
}

err goes through the debug-mode log() but isn't passed to createError. As far as I can tell, downstream observability (Sentry in our case) only sees the message string β€” there's no way to distinguish a timeout from a connection reset from a TLS handshake failure.

I might be wrong about this being unintentional β€” happy to be corrected if there's a reason err is stripped that I'm missing.

πŸ› οΈ To reproduce

I can put together a Stackblitz that points a proxied script at a deliberately slow upstream (e.g. httpbin.org/delay/30) to trip the 15s abort and inspect the resulting H3Error β€” let me know if that's needed before triage. The issue itself is visible in the source citation above.

🌈 Expected behavior

The underlying error attached as cause (and optionally name/code surfaced in data) so error trackers can group and filter:

throw createError({
  statusCode: 502,
  statusMessage: "Bad Gateway",
  message: `Proxy upstream request failed: ${targetUrl}`,
  cause: err,
  data: { errorName: err?.name, errorCode: err?.code },
});

H3's cause isn't serialized to the client response, so this should be safe β€” but you'd know better than me whether there's a reason to keep it stripped.

ℹ️ Additional context

We're seeing a high volume of these in production (~800k/quarter, mostly from iOS in-app webviews proxying Meta Pixel) and have been stuck because the message alone doesn't tell us whether to look at the 15s timeout, network reset patterns, or something else.

Related context: I filed #639 about the v0 graceful-204 fallback being removed; that's resolved and 502s are the documented behavior now, which is fine β€” this issue is purely about making the 502 actionable.

Happy to send a PR if the change above looks right to you.

Environment: @nuxt/scripts@1.0.6, Nitro on Vercel (Node 22).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions