Skip to content

Commit 3f3d1c4

Browse files
nickwesselmanclaude
andcommitted
Fix renderSingleTask error swallowing on sequential calls
The setImmediate deferral of unmountInk() introduced a race condition when renderSingleTask is called sequentially: the first instance's deferred unmountInk() can fire after the second instance starts, causing the second waitUntilExit() to resolve prematurely. If the second task then throws, the error is silently swallowed because render().catch(reject) never fires. Fix: add onError callback to SingleTask (mirroring onComplete) so errors are propagated directly via the callback rather than relying on waitUntilExit() rejection, which is unreliable across sequential ink renders. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 758be51 commit 3f3d1c4

2 files changed

Lines changed: 5 additions & 3 deletions

File tree

packages/cli-kit/src/private/node/ui/components/SingleTask.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,12 @@ interface SingleTaskProps<T> {
99
title: TokenizedString
1010
task: (updateStatus: (status: TokenizedString) => void) => Promise<T>
1111
onComplete?: (result: T) => void
12+
onError?: (error: Error) => void
1213
onAbort?: () => void
1314
noColor?: boolean
1415
}
1516

16-
const SingleTask = <T,>({task, title, onComplete, onAbort, noColor}: SingleTaskProps<T>) => {
17+
const SingleTask = <T,>({task, title, onComplete, onError, onAbort, noColor}: SingleTaskProps<T>) => {
1718
const [status, setStatus] = useState(title)
1819
const [isDone, setIsDone] = useState(false)
1920
const {exit: unmountInk} = useApp()
@@ -41,9 +42,10 @@ const SingleTask = <T,>({task, title, onComplete, onAbort, noColor}: SingleTaskP
4142
})
4243
.catch((error) => {
4344
setIsDone(true)
45+
onError?.(error)
4446
setImmediate(() => unmountInk(error))
4547
})
46-
}, [task, unmountInk, onComplete])
48+
}, [task, unmountInk, onComplete, onError])
4749

4850
if (isDone) {
4951
// clear things once done

packages/cli-kit/src/public/node/ui.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ export async function renderSingleTask<T>({
522522
renderOptions,
523523
}: RenderSingleTaskOptions<T>): Promise<T> {
524524
return new Promise<T>((resolve, reject) => {
525-
render(<SingleTask title={title} task={task} onComplete={resolve} onAbort={onAbort} />, {
525+
render(<SingleTask title={title} task={task} onComplete={resolve} onError={reject} onAbort={onAbort} />, {
526526
...renderOptions,
527527
exitOnCtrlC: false,
528528
}).catch(reject)

0 commit comments

Comments
 (0)