Repository navigation
Drop input that belongs to a run that already ended - #1164
Merged
Merged
Conversation
Runner.provideInput forwarded every input to SyncClient.writeMessage, which throws "No active call to send a message to." once the call has ended. Nothing awaits provideInput, so the rejection went unhandled. It now skips the write when the client has no active call, and ignores that same error when the call ends while the write waits for the worker to start reading. Input requests reach the main thread on the event port and the end of the call on the worker port, so a request can still arrive after a run was stopped or after the next run began. InputOutput now only accepts input requests between the worker's start event and the end of the run, so a stale request no longer opens the prompt, sends a buffered line or puts the runner in AwaitingInput. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0155VQF6bMAnbEaGnc1haWMp
bmesuere
commented
Sep 25, 2026
bmesuere
left a comment
Member
Author
There was a problem hiding this comment.
馃 Reviewed. This addresses a real cross-port race: input can be submitted or delivered after the active call has ended, producing an unhandled rejection and, in batch mode, potentially changing UI state. The SyncClient state guard handles the write race, while the start/end window prevents stale input events from opening a prompt or consuming buffered input. Event ordering on the shared event callback keeps an old run's input before a new run's RunCode event. Focused tests, source lint, and typecheck pass locally. No blocking findings.
TomNaessens
approved these changes
Oct 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request stops Papyros from sending input to a run that has already ended, which rejected unhandled with
Error: No active call to send a message to.Sentry: DODONA-FRONTEND-2ER (4 users, Papyros 4.13.3). Two of the three breadcrumb trails start the debugger, close it and start it again within seconds; the third submits input as a run ends.
Cause.
InputOutput.provideInputcallsRunner.provideInputwithout awaiting it, andRunner.provideInputforwards toSyncClient.writeMessage, which throws when the client is idle or when the call ends while the write is queued behind the worker'sreadingstatus. Input events reach the main thread on the event port and the end of the call on the worker's own port, so the two are not ordered: an input request can arrive afterstop()closed the prompt (an interrupted run sends no end event to close it again), or after the nextstart()reset the input state. In batch mode such a request sends the next buffered line straight away.Runner.provideInputreturns without writing when theSyncClienthas no active call, and ignores the error when the call ends while the write is queued. Any other write error still rejects. The check uses the client's state rather thanRunState, sinceRunStatealso tracks package loading and is set by the same unordered events.InputOutputaccepts input requests only between the worker'sRunCodestart event and the end of the run (onRunEnd, which the end event,stop(), failed runs and runtime recovery all call). The worker sends its start event before any input request of that run on the same port, the same orderingDebuggerrelies on for stale frames. Outside that window an input request opens no prompt, sends no buffered line, and no longer moves the runner toAwaitingInput.Manual testing instructions
The failure depends on message timing between two ports, so it does not reproduce on demand; the tests below reproduce both paths deterministically.
print(input())in interactive and in batch mode, with and without the debugger, and in batch mode with several buffered lines. Input is read as before.Checklist
InputOutput.test.ts, both fail before this change: input submitted after a stopped run resolves without writing and leaves the runnerReady(before: the Sentry error as an unhandled rejection); an input request published after the run ended sends no buffered line and leaves the runnerReady.Reaches production once Dodona bumps
@dodona/papyros.