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
5 changes: 5 additions & 0 deletions .changeset/handle-tracing-processor-replacement-rejections.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@openai/agents-core': patch
---

fix: handle rejected tracing processor shutdowns during processor replacement
8 changes: 7 additions & 1 deletion packages/agents-core/src/tracing/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,13 @@ export class MultiTracingProcessor implements TracingProcessor {
setProcessors(processors: TracingProcessor[]): void {
logger.debug('Shutting down old processors');
for (const processor of this.#processors) {
processor.shutdown();
void processor.shutdown().catch((error) => {
logModelAndToolActionError(
logger,
'Error shutting down replaced tracing processor',
error,
);
});
}
this.#processors = processors;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/agents-core/test/tracing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1624,9 +1624,9 @@ describe('withTrace & span helpers (integration)', () => {
describe('MultiTracingProcessor', () => {
it('should call all processors shutdown when setting new processors', () => {
const processor1 = new TestProcessor();
processor1.shutdown = vi.fn();
processor1.shutdown = vi.fn(async () => {});
const processor2 = new TestProcessor();
processor2.shutdown = vi.fn();
processor2.shutdown = vi.fn(async () => {});
const multiProcessor = new MultiTracingProcessor();
multiProcessor.setProcessors([processor1]);
expect(processor1.shutdown).not.toHaveBeenCalled();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { describe, expect, it, vi } from 'vitest';

import coreLogger from '../src/logger';
import {
MultiTracingProcessor,
type TracingProcessor,
} from '../src/tracing/processor';
import type { Span } from '../src/tracing/spans';
import { Trace } from '../src/tracing/traces';

class BaseProcessor implements TracingProcessor {
tracesStarted: Trace[] = [];

async onTraceStart(trace: Trace): Promise<void> {
this.tracesStarted.push(trace);
}

async onTraceEnd(_trace: Trace): Promise<void> {}

async onSpanStart(_span: Span<any>): Promise<void> {}

async onSpanEnd(_span: Span<any>): Promise<void> {}

async shutdown(): Promise<void> {}

async forceFlush(): Promise<void> {}
}

class RejectingShutdownProcessor extends BaseProcessor {
shutdown(): Promise<void> {
return Promise.reject(new Error('shutdown failed'));
}
}

describe('tracing processor replacement failures', () => {
it('handles rejected old-processor shutdowns while installing replacements', async () => {
const errorSpy = vi.spyOn(coreLogger, 'error').mockImplementation(() => {});
const multiProcessor = new MultiTracingProcessor();
const oldProcessor = new RejectingShutdownProcessor();
const replacement = new BaseProcessor();
multiProcessor.addTraceProcessor(oldProcessor);

expect(() => multiProcessor.setProcessors([replacement])).not.toThrow();
await Promise.resolve();
await Promise.resolve();

expect(
errorSpy.mock.calls.some(
([message]) =>
message === 'Error shutting down replaced tracing processor',
),
).toBe(true);

const trace = new Trace({ name: 'replacement-check' }, replacement);
await multiProcessor.onTraceStart(trace);
expect(replacement.tracesStarted).toEqual([trace]);

errorSpy.mockRestore();
});
});