Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 4 additions & 1 deletion packages/browser/src/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,10 @@ async function registerPlugins(
options,
tsubMiddleware,
pluginSources
).catch(() => [])
).catch((err) => {
console.error('Failed to load remote plugins', err)
return [] as Plugin[]
})

const basePlugins = [envEnrichment, ...legacyDestinations, ...remotePlugins]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import { AMPLITUDE_WRITEKEY } from '../../../test-helpers/test-writekeys'
import { PersistedPriorityQueue } from '../../../lib/priority-queue/persisted'
import * as Factory from '../../../test-helpers/factories'
import { cdnSettingsMinimal } from '../../../test-helpers/fixtures'
import * as MetricHelpers from '../../../core/stats/metric-helpers'
import * as ScriptLoader from '../../../lib/load-script'

const cdnResponse: CDNSettings = {
...cdnSettingsMinimal,
Expand Down Expand Up @@ -838,3 +840,65 @@ describe('option overrides', () => {
)
})
})

describe('load error metrics', () => {
let metricSpy: jest.SpyInstance

beforeEach(() => {
jest.restoreAllMocks()
jest.resetAllMocks()
metricSpy = jest.spyOn(MetricHelpers, 'recordIntegrationMetric')
})

it('records integration invoke error metric when loadIntegration fails', async () => {
jest
.spyOn(ScriptLoader, 'loadScript')
.mockRejectedValue(new Error('Script load failed'))

const dest = new LegacyDestination(
'Broken Integration',
'latest',
'writeKey',
{},
{}
)

const ctx = Context.system()

await expect(dest.load(ctx, {} as Analytics)).rejects.toThrow(
'Script load failed'
)

expect(metricSpy).toHaveBeenCalledWith(ctx, {
integrationName: 'Broken Integration',
methodName: 'load',
type: 'classic',
didError: true,
})
})

it('records integration invoke error metric when buildIntegration fails', async () => {
// Provide an integrationSource that will cause buildIntegration to throw
const badSource = {} as any

const dest = new LegacyDestination(
'Bad Build Integration',
'latest',
'writeKey',
{},
{},
badSource
)

const ctx = Context.system()

await expect(dest.load(ctx, {} as Analytics)).rejects.toThrow()

expect(metricSpy).toHaveBeenCalledWith(ctx, {
integrationName: 'Bad Build Integration',
methodName: 'load',
type: 'classic',
didError: true,
})
})
})
38 changes: 24 additions & 14 deletions packages/browser/src/plugins/ajs-destination/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,20 +134,30 @@ export class LegacyDestination implements InternalPluginWithAddMiddleware {
return
}

const integrationSource =
this.integrationSource ??
(await loadIntegration(
ctx,
this.name,
this.version,
this.options.obfuscate
))

this.integration = buildIntegration(
integrationSource,
this.settings,
analyticsInstance
)
try {
const integrationSource =
this.integrationSource ??
(await loadIntegration(
ctx,
this.name,
this.version,
this.options.obfuscate
))

this.integration = buildIntegration(
integrationSource,
this.settings,
analyticsInstance
)
} catch (error) {
recordIntegrationMetric(ctx, {
integrationName: this.name,
methodName: 'load',
type: 'classic',
didError: true,
})
throw error
}

this.onReady = new Promise((resolve) => {
const onReadyFn = (): void => {
Expand Down
37 changes: 37 additions & 0 deletions packages/browser/src/plugins/remote-loader/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { InitOptions } from '../../../core/analytics'
import { Context } from '../../../core/context'
import { tsubMiddleware } from '../../routing-middleware'
import { cdnSettingsMinimal } from '../../../test-helpers/fixtures'
import * as MetricHelpers from '../../../core/stats/metric-helpers'

const pluginFactory = jest.fn()

Expand Down Expand Up @@ -966,4 +967,40 @@ describe('Remote Loader', () => {

expect(newCtx.event.name).toEqual('foobar')
})

it('records integration invoke error metric when plugin fails to load', async () => {
const metricSpy = jest.spyOn(MetricHelpers, 'recordIntegrationMetric')

// @ts-expect-error not gonna return a script tag sorry
jest.spyOn(loader, 'loadScript').mockImplementation(() => {
window['flaky'] = (): never => {
throw Error('aaay')
}
return Promise.resolve(true)
})

await remoteLoader(
{
...cdnSettingsMinimal,
remotePlugins: [
{
name: 'flaky plugin',
creationName: 'Flaky Plugin',
url: 'cdn/path/to/flaky.js',
libraryName: 'flaky',
settings: {},
},
],
},
{},
{}
)

expect(metricSpy).toHaveBeenCalledWith(expect.any(Context), {
integrationName: 'Flaky Plugin',
methodName: 'load',
type: 'action',
didError: true,
})
})
})
6 changes: 6 additions & 0 deletions packages/browser/src/plugins/remote-loader/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,12 @@ export async function remoteLoader(
}
} catch (error) {
console.warn('Failed to load Remote Plugin', error)
recordIntegrationMetric(Context.system(), {
integrationName: remotePlugin.creationName,
methodName: 'load',
type: 'action',
didError: true,
})
Comment on lines 311 to +317
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

The new error metric uses integrationName: remotePlugin.creationName, but action destination metrics elsewhere in this module use integrationName: this.action.name (i.e., the plugin's name). This means load failures will be tagged under a different integration_name than successful load/invoke metrics for the same destination, making dashboards and error-rate calculations inconsistent. Consider using remotePlugin.name here for consistency, or update the ActionDestination metric calls to use the wrapper/destination name consistently (e.g., this.name / remotePlugin.creationName).

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

@copilot open a new pull request to apply changes based on this feedback

}
}
)
Expand Down
Loading
⚔