Skip to content

Commit bdaf8c6

Browse files
committed
fix(mothership): keep waiting on a workflow tool's run when its log cannot be read
1 parent efe6170 commit bdaf8c6

2 files changed

Lines changed: 33 additions & 1 deletion

File tree

‎apps/sim/lib/mothership/tools/handlers/workflow/mutations.test.ts‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,24 @@ describe('a workflow tool run past its wait', () => {
530530
run.settle({ success: true, output: {}, logs: [] })
531531
})
532532

533+
it('keeps waiting on a run past its wait when its log cannot be read', async () => {
534+
const run = pendingRun()
535+
mocks.logStatus.mockReset().mockRejectedValue(new Error('db down'))
536+
let answered = false
537+
const answer = executeRunWorkflow(
538+
{ workflowId: 'workflow-1' },
539+
{ ...context, workflowRunWaitMs: 1_000 }
540+
).then((result) => {
541+
answered = true
542+
return result
543+
})
544+
await vi.advanceTimersByTimeAsync(5_000)
545+
expect(answered).toBe(false)
546+
const executionId = run.lifecycle().executionId
547+
run.settle({ success: true, output: { value: 3 }, logs: [], metadata: { executionId } })
548+
await expect(answer).resolves.toMatchObject({ success: true, output: { output: { value: 3 } } })
549+
})
550+
533551
it('answers with the result when the run settles within its wait', async () => {
534552
const run = pendingRun()
535553
const answer = executeRunBlock(

‎apps/sim/lib/mothership/tools/handlers/workflow/mutations.ts‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { createLogger } from '@sim/logger'
2+
import { toError } from '@sim/utils/errors'
23
import { interruptibleSleep } from '@sim/utils/helpers'
34
import { generateId } from '@sim/utils/id'
45
import { filterUndefined, isPlainRecord, isRecordLike } from '@sim/utils/object'
@@ -261,6 +262,19 @@ function copilotRunLifecycle(context: ExecutionContext) {
261262
/** How often a run past its wait is re-read for its log before it is answered running. */
262263
const RUN_LOG_POLL_MS = 1000
263264

265+
/** Whether the run's log exists; a lookup that fails counts as not yet, so the call keeps waiting. */
266+
async function runIsReadable(executionId: string, workflowId: string): Promise<boolean> {
267+
try {
268+
return (await getWorkflowExecutionLogStatus(executionId, workflowId)) !== undefined
269+
} catch (error) {
270+
logger.warn('Could not read a workflow tool run past its wait; still waiting on it', {
271+
executionId,
272+
error: toError(error).message,
273+
})
274+
return false
275+
}
276+
}
277+
264278
/**
265279
* Runs a workflow tool's run and answers with its result. When the call names a wait the run
266280
* outlasts (`workflowRunWaitMs`), it answers the run still running by the execution id it gave
@@ -284,7 +298,7 @@ async function answerWithinWait(
284298
let result = await Promise.race([settled, waited(waitMs)])
285299
// Answered running only once the run is readable by its id; one still starting up (or in
286300
// preflight) is waited on until its log exists or it settles.
287-
while (!result && !(await getWorkflowExecutionLogStatus(executionId, workflowId))) {
301+
while (!result && !(await runIsReadable(executionId, workflowId))) {
288302
result = await Promise.race([settled, waited(RUN_LOG_POLL_MS)])
289303
}
290304
if (result) return result

0 commit comments

Comments
 (0)