Skip to content

feat(api): forward original Error to Faro's beforeSend via originalError - #53

Merged
sindrerh2 merged 2 commits into
betafrom
feature/beta-sync
Sep 11, 2026
Merged

feat(api): forward original Error to Faro's beforeSend via originalError#53
sindrerh2 merged 2 commits into
betafrom
feature/beta-sync

Conversation

@sindrerh2

@sindrerh2 sindrerh2 commented Sep 10, 2026

Copy link
Copy Markdown

What

captureException() now forwards the real Error instance to Faro's pushError as the originalError option whenever the input is a genuine Error. Combined with init({ faro: { preserveOriginalError: true } }), this makes the original thrown instance available on item.payload.originalError inside a custom beforeSend hook, before Faro strips it prior to sending to the collector.

This mirrors how Faro's own auto-capture instrumentations (window.onerror, unhandledrejection) already thread originalError through — @nais/apm's captureException() wrapper previously did not, which meant preserveOriginalError: true had no effect for manually-reported exceptions.

Coerced non-Error values (e.g. plain strings) do not get an originalError, since there's nothing more original than the synthesized Error itself.

Why

A consumer team asked for a way to centrally branch on error type/shape (e.g. instanceof SomeCustomError) in one place before reporting to apm, using Faro's existing preserveOriginalError escape hatch. That option only worked for Faro's own automatic instrumentations, not for captureException() calls.

Changes

  • src/api.ts: pass originalError to faro.api.pushError() when error is an Error instance
  • src/api.test.ts / src/index.test.ts: updated/extended tests to assert originalError is forwarded (and not forwarded for coerced non-Error values)
  • README.md: documented the pattern with an example beforeSend hook

Testing

  • pnpm test — all 230 tests pass (22 test files)

Copilot AI lite review requested due to automatic review settings September 10, 2026 09:25

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

captureException() can throw at runtime when called with a non-Error plus context/fingerprint because the new object-spread logic may spread undefined into options_.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR improves @nais/apm’s manual exception capture by forwarding the original thrown Error instance into Faro’s pushError as originalError, enabling beforeSend logic to inspect item.payload.originalError when preserveOriginalError: true is enabled. It also adds beta-branch operational tooling/workflows and documents the new error-handling pattern.

Changes:

  • Forward originalError from captureException() to Faro pushError() when the input is a real Error.
  • Update/extend tests and README to cover/document originalError forwarding behavior.
  • Add/update GitHub workflows and release docs for beta sync and beta publishing constraints.
File summaries
File Description
src/api.ts Adds originalError forwarding and reworks pushError options construction.
src/api.test.ts Updates expectations for originalError and adds assertions around non-Error coercion.
src/index.test.ts Updates integration-level expectation for pushError options to include originalError.
README.md Documents how to use preserveOriginalError + beforeSend to branch on error type/shape.
RELEASING.md Adds process guidance for keeping beta in sync and protecting beta branch history.
.github/workflows/sync-beta.yml Introduces workflow to open a “sync main into beta” PR on each main push.
.github/workflows/release-please-beta.yml Tightens manual beta publish to only allow workflow_dispatch from beta.
.github/workflows/ci.yaml Runs CI on PRs targeting beta and pins core actions to commit SHAs.
Review details
  • Files reviewed: 8/8 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/api.ts
Comment on lines +102 to +104
const originalError = error instanceof Error ? error : undefined;
const hasContext = Object.keys(context).length > 0;
const options_ = hasContext || originalError ? { ...(hasContext && { context }), ...(originalError && { originalError }) } : undefined;
Comment thread src/api.test.ts
Comment on lines +77 to 83
it('coerces non-Error values and does not forward originalError', () => {
captureException('plain string failure');
const pushed = api.pushError.mock.calls[0]?.[0] as Error;
expect(pushed).toBeInstanceOf(Error);
expect(pushed.message).toBe('plain string failure');
expect(api.pushError.mock.calls[0]?.[1]).toBeUndefined();
});
@Starefossen
Starefossen changed the base branch from main to beta September 10, 2026 09:30
Starefossen and others added 2 commits September 10, 2026 11:43
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
captureException() now passes error as originalError in the pushError
options whenever the input is a genuine Error instance. Combined with
init({ faro: { preserveOriginalError: true } }), the real Error
instance is available on item.payload.originalError inside a custom
beforeSend hook, before Faro strips it prior to sending to the
collector. This lets callers centrally branch on error type/shape
before reporting, matching Faro's own auto-capture instrumentations
which already thread originalError through in the same way.

Coerced non-Error values (e.g. plain strings) do not get an
originalError, since there is nothing more original than the
synthesized Error itself.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@sindrerh2 sindrerh2 changed the title feat(api): forward original Error to Faro's beforeSend via originalError feat: originalError forwarding + exported markErrorCaptured for custom error boundaries Sep 10, 2026
@sindrerh2 sindrerh2 changed the title feat: originalError forwarding + exported markErrorCaptured for custom error boundaries feat(api): forward original Error to Faro's beforeSend via originalError Sep 10, 2026
@sindrerh2
sindrerh2 merged commit 2e4766c into beta Sep 11, 2026
9 checks passed
@sindrerh2
sindrerh2 deleted the feature/beta-sync branch September 11, 2026 07:29
sindrerh2 pushed a commit that referenced this pull request Sep 11, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.7.0-beta.1](apm-v0.6.4-beta.1...apm-v0.7.0-beta.1)
(2026-09-11)


### Features

* **api:** forward original Error to Faro's beforeSend via originalError
([#53](#53))
([2e4766c](2e4766c))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants