diff --git a/packages/workflow-executor/src/executors/base-step-executor.ts b/packages/workflow-executor/src/executors/base-step-executor.ts index 8f6de37859..885d680a3f 100644 --- a/packages/workflow-executor/src/executors/base-step-executor.ts +++ b/packages/workflow-executor/src/executors/base-step-executor.ts @@ -173,9 +173,11 @@ export default abstract class BaseStepExecutor { + if (!hasTimeoutFired) return; this.context.logger.info('Step work rejected after timeout — result discarded', { runId: this.context.runId, stepId: this.context.stepId, @@ -188,7 +190,10 @@ export default abstract class BaseStepExecutor((_, reject) => { - timer = setTimeout(() => reject(new StepTimeoutError(timeoutMs)), timeoutMs); + timer = setTimeout(() => { + hasTimeoutFired = true; + reject(new StepTimeoutError(timeoutMs)); + }, timeoutMs); }), ]); } finally { diff --git a/packages/workflow-executor/test/executors/base-step-executor.test.ts b/packages/workflow-executor/test/executors/base-step-executor.test.ts index 1b0f16bb1e..6cd5c51af0 100644 --- a/packages/workflow-executor/test/executors/base-step-executor.test.ts +++ b/packages/workflow-executor/test/executors/base-step-executor.test.ts @@ -453,6 +453,21 @@ describe('BaseStepExecutor', () => { process.off('unhandledRejection', unhandled); } }, 5_000); + + it('does not log discard message when step rejects before timeout', async () => { + const logger = makeMockLogger(); + const executor = new TestableExecutor( + makeContext({ stepTimeoutMs: 5_000, logger }), + new Error('normal step error'), + ); + + await executor.execute(); + + expect(logger.info).not.toHaveBeenCalledWith( + 'Step work rejected after timeout — result discarded', + expect.anything(), + ); + }); }); describe('activity log lifecycle', () => {