Skip to content
Open
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
39 changes: 39 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,40 @@
# Rust
/target
*.rs.bk

# Node.js
node_modules/
dist/
out/
build/
*.tsbuildinfo

# Environment files
.env
.env.*
!.env.example

# Logs
logs/
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# OS files
.DS_Store
Thumbs.db
*.swp
*.swo
*~

# IDE/Editor
.idea/
.vscode/
*.sublime-project
*.sublime-workspace

# Coverage & Testing
coverage/
.nyc_output/
*.lcov
79 changes: 79 additions & 0 deletions sentry-javascript/19014/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Reproduction for sentry-javascript#19014

**Issue:** https://github.com/getsentry/sentry-javascript/issues/19014

## Description

When using a custom class that extends `AggregateError` with a different `name` property, Sentry fails to detect it as an exception group. This is because the SDK checks `error.name === "AggregateError"` instead of using `error instanceof AggregateError`.

## Steps to Reproduce

1. Set your Sentry DSN:
```bash
export SENTRY_DSN="your-dsn-here"
```

2. Install dependencies:
```bash
npm install
```

3. Run the reproduction:
```bash
npm start
```

## Expected Behavior

Both the standard `AggregateError` and the `CustomAggregateError` (which extends `AggregateError`) should be detected as exception groups, with `is_exception_group: true` in the event mechanism.

The "Related Exceptions" section should appear in the Sentry UI for both errors.

## Actual Behavior

- **Standard AggregateError**: Correctly detected as exception group (`is_exception_group: true`)
- **CustomAggregateError**: NOT detected as exception group (`is_exception_group: undefined`)

The console output shows:
```
=== Test 1: Standard AggregateError ===
Exception values:
[0] type: Error
is_exception_group: undefined
parent_id: 0
exception_id: 2
[1] type: Error
is_exception_group: undefined
parent_id: 0
exception_id: 1
[2] type: AggregateError
is_exception_group: true ✓ Correct - detected as exception group

=== Test 2: CustomAggregateError ===
Exception values:
[0] type: Error
...
[4] type: CustomAggregateError
is_exception_group: undefined ✗ BUG - not detected as exception group
```

## Root Cause

The issue is in [`packages/core/src/utils/aggregate-errors.ts`](https://github.com/getsentry/sentry-javascript/blob/809d578d7d9281123d474105eaa9952e5fb1a571/packages/core/src/utils/aggregate-errors.ts#L101-L109):

```javascript
// Current implementation checks the name property
if (error.name === 'AggregateError') {
// ...
}

// Should check instanceof instead
if (error instanceof AggregateError) {
// ...
}
```

## Environment

- Node.js: v18+
- @sentry/node: 10.27.0
73 changes: 73 additions & 0 deletions sentry-javascript/19014/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import * as Sentry from '@sentry/node';

Sentry.init({
dsn: process.env.SENTRY_DSN || 'https://test@test.ingest.sentry.io/123',
// Use default integrations which include linkedErrors for exception grouping
tracesSampleRate: 1.0,
sendDefaultPii: true,
// Disable sending to Sentry for local testing
beforeSend(event) {
console.log('\n--- Sentry Event ---');
console.log('Exception values:');
event.exception?.values?.forEach((exc, i) => {
console.log(` [${i}] type: ${exc.type}`);
console.log(` is_exception_group: ${exc.mechanism?.is_exception_group}`);
console.log(` parent_id: ${exc.mechanism?.parent_id}`);
console.log(` exception_id: ${exc.mechanism?.exception_id}`);
});
// Return null to prevent sending when no real DSN is configured
if (!process.env.SENTRY_DSN) {
return null;
}
return event;
},
});

// Custom AggregateError class with a different name property
// This is common practice to differentiate error types by domain/source
class CustomAggregateError extends AggregateError {
constructor(errors, message, options) {
super(errors, message, options);
this.name = 'CustomAggregateError';
}
}

// --- Test 1: Standard AggregateError (works correctly) ---
console.log('=== Test 1: Standard AggregateError ===');
console.log('Creating AggregateError with name="AggregateError"');

const standardAggregate = new AggregateError(
[new Error('error 1'), new Error('error 2')],
'standard aggregate error'
);

Sentry.captureException(standardAggregate);

// --- Test 2: Custom AggregateError (BUG - not detected as exception group) ---
console.log('\n=== Test 2: CustomAggregateError ===');
console.log('Creating CustomAggregateError with name="CustomAggregateError"');
console.log('BUG: This should be detected as an exception group but is NOT');

const customAggregate = new CustomAggregateError(
[
new Error('error 1', { cause: new Error('error 1 cause') }),
new Error('error 2'),
],
'custom aggregate error',
{ cause: new Error('aggregate cause') }
);

console.log(`\nError instanceof AggregateError: ${customAggregate instanceof AggregateError}`);
console.log(`Error name: ${customAggregate.name}`);
console.log(`Error has 'errors' property: ${Array.isArray(customAggregate.errors)}`);

Sentry.captureException(customAggregate);

// Flush to ensure events are sent
await Sentry.flush(2000);

console.log('\n=== Summary ===');
console.log('Expected: Both errors should have is_exception_group: true');
console.log('Actual: Only the standard AggregateError has is_exception_group: true');
console.log('\nThe issue is that Sentry checks error.name === "AggregateError"');
console.log('instead of error instanceof AggregateError');
Loading
Loading