Skip to content

fix(claude): handle async background task notifications in remote mode#354

Open
xyzhang626 wants to merge 4 commits intotiann:mainfrom
xyzhang626:fix/async-claude-messages
Open

fix(claude): handle async background task notifications in remote mode#354
xyzhang626 wants to merge 4 commits intotiann:mainfrom
xyzhang626:fix/async-claude-messages

Conversation

@xyzhang626
Copy link
Contributor

Summary

  • Fix Claude remote session blocking on async background task notifications (e.g. delayed responses from "say hi after 10 secs")
  • The stream reader was blocking on pendingNextInput even when async task results arrived, causing notifications to be silently dropped

Problem

When Claude executes a background task (e.g. say hi after 10 secs), the task notification arrives asynchronously after the main response completes. The remote stream handler was waiting for pendingNextInput to resolve before processing new messages, which caused these async notifications to never be delivered to the user.

Reproduce

  1. Start a Claude session in remote mode
  2. Send: "say hi after 10 secs"
  3. Before this fix: the delayed message is silently dropped
  4. After this fix: the delayed message is correctly delivered

Changes

  • claudeRemote.ts: Refactor stream processing to not block on pendingNextInput when async task results arrive. Add debug traces for task notification lifecycle.
  • claudeRemoteLauncher.ts: Add debug logging for incoming messages in the launcher bridge.
  • claudeRemote.test.ts: Add test cases covering the async notification flow.

Test plan

  • Run bun test claudeRemote.test.ts
  • Start a remote Claude session, send "say hi after 10 secs", verify the delayed message arrives
  • Verify normal synchronous message flow is unaffected

Xiaoyi and others added 3 commits March 24, 2026 05:42
The test was failing in CI because Claude CLI is not installed in the
CI environment. Mock the path utility to avoid the dependency.

via [HAPI](https://hapi.run)

Co-Authored-By: HAPI <noreply@hapi.run>
Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review mode: initial

Findings: 1 major. See inline comment.

Testing: Not run (automation)

logger.debug(`${debugPrefix} scheduleNextMessage start fetchId=${fetchId}`);
void (async () => {
try {
const next = await opts.nextMessage();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MAJOR] Unhandled rejection risk from fire-and-forget nextMessage. Detached async call has no catch, so any rejection bypasses the outer try/catch and becomes unhandled. Evidence: cli/src/claude/claudeRemote.ts:190.

Suggested fix:

        void (async () => {
            try {
                const next = await opts.nextMessage();
                if (!next) {
                    inputEnded = true;
                    messages.end();
                    logger.debug(
                        `${debugPrefix} nextMessage resolved null fetchId=${fetchId} elapsedMs=${Date.now() - startedAt}; input ended`
                    );
                    return;
                }
                mode = next.mode;
                messages.push({ type: 'user', message: { role: 'user', content: next.message } });
                logger.debug(
                    `${debugPrefix} nextMessage resolved fetchId=${fetchId} elapsedMs=${Date.now() - startedAt} ` +
                    `messageLength=${next.message.length} permissionMode=${next.mode.permissionMode}`
                );
            } catch (e) {
                inputEnded = true;
                messages.end();
                logger.debug(`${debugPrefix} nextMessage error fetchId=${fetchId}`, e);
            } finally {
                nextMessageFetchInFlight = false;
                logger.debug(`${debugPrefix} scheduleNextMessage done fetchId=${fetchId}`);
            }
        })();

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review mode: initial
No issues found in modified lines.
Residual risk: tests not run in this review.
Testing: Not run (automation).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant