diff --git a/packages/core/lib/v3/flowlogger/FlowLogger.ts b/packages/core/lib/v3/flowlogger/FlowLogger.ts index 38c974924..9ced6ac12 100644 --- a/packages/core/lib/v3/flowlogger/FlowLogger.ts +++ b/packages/core/lib/v3/flowlogger/FlowLogger.ts @@ -413,7 +413,15 @@ export class FlowLogger { parentEvents: [], }; - loggerContext.enterWith(ctx); + try { + loggerContext.enterWith(ctx); + } catch { + // Some runtimes (e.g. Cloudflare Workers) do not implement enterWith() + // because it mutates context across concurrent requests, which is unsafe + // in that environment. Fall through: the context is still returned and + // callers that need ALS can use loggerContext.run() via runWithLogging() + // or withContext(). + } return ctx; } diff --git a/packages/core/tests/unit/flowlogger-capturing-llm.test.ts b/packages/core/tests/unit/flowlogger-capturing-llm.test.ts index 6bdf375c7..085e1d37b 100644 --- a/packages/core/tests/unit/flowlogger-capturing-llm.test.ts +++ b/packages/core/tests/unit/flowlogger-capturing-llm.test.ts @@ -1,5 +1,7 @@ +import { AsyncLocalStorage } from "node:async_hooks"; import { describe, expect, it } from "vitest"; import { FlowLogger } from "../../lib/v3/flowlogger/FlowLogger.js"; +import { EventEmitterWithWildcardSupport } from "../../lib/v3/flowlogger/EventEmitter.js"; describe("flow logger llm logging", () => { it("no-ops direct llm logging calls when no flow context is active", () => { @@ -47,4 +49,30 @@ describe("flow logger llm logging", () => { text: "done", }); }); + + it("FlowLogger.init() does not throw when enterWith() is not implemented (e.g. Cloudflare Workers)", () => { + // Simulate a runtime that omits enterWith() from AsyncLocalStorage. + const originalEnterWith = AsyncLocalStorage.prototype.enterWith; + Object.defineProperty(AsyncLocalStorage.prototype, "enterWith", { + value: undefined, + configurable: true, + writable: true, + }); + + try { + const bus = new EventEmitterWithWildcardSupport(); + let ctx: ReturnType | undefined; + expect(() => { + ctx = FlowLogger.init("session-cloudflare", bus); + }).not.toThrow(); + // The returned context must still be valid even without ALS support. + expect(ctx).toMatchObject({ sessionId: "session-cloudflare" }); + } finally { + Object.defineProperty(AsyncLocalStorage.prototype, "enterWith", { + value: originalEnterWith, + configurable: true, + writable: true, + }); + } + }); });