You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(mobile): a dropped connection no longer discards a dictation - #35567
On mobile, a dictation sent to an environment is deleted when the connection drops. The recording stops, no error is shown, and after reconnecting there is nothing to retry, so the whole message has to be spoken again.
To reproduce: choose an environment for voice input in Settings → Voice, start dictating, then turn on airplane mode.
Change
The voice route changes whenever the connection drops or comes back, and VoiceInputProvider cancelled the dictation on every route change. Cancelling mid-recording deletes the audio file without any message. A recording that survived anyway still could not be sent: its transcriber was tied to the connection object it was prepared on, and a reconnect replaces that object, so the send failed with "Your transcription choice changed."
Only a change of voice destination (the grant) cancels a dictation. A reconnect no longer does.
The environment transcriber looks up the current connection each time it sends. A recording made across a reconnect goes to the same chosen environment. With no connection, sending fails with "Connection lost. Your recording is saved; retry once reconnected."
Mobile turns on retryFailedRecording, as web already does. A failed send keeps the audio, and the existing retry (the mic button, or Retry in the voice control) sends it again without re-recording. Dismissing the error still deletes it.
On-device transcription uses the live start path, so its recordings are not kept and its behaviour is unchanged. The one exception is Android builds without the live recorder, where a failed on-device transcription is now kept for retry, matching web.
Scope
A bug fix. I hit it as a daily user: a dropped connection mid-dictation lost the whole message. I found no issue or open PR for it, and it needs none because losing a recording without a message is plainly unintended and the reproduction above takes a minute.
The change is in shared mobile code, so it covers iOS and Android. Web loses a dictation the same way: useEnvironmentSpeechInput disposes its controller whenever the connection object changes, and in streaming mode it keeps no local copy of the audio. That code is separate, so it is not changed here.
Verification
Test. Added a case to voiceDestination.test.ts: prepare a recording, drop the connection, send (it fails with the "saved" error), reconnect, send again (it transcribes). On main it fails with code: "cancelled" ("Your transcription choice changed."). With the fix it passes.
Android emulator (API 35, arm64). Dev client built from this branch, an isolated dev server with the Canary 180M local model, and airplane mode to drop the connection. I drove the emulator with adb rather than T3's device panel, because agent device access was off in my session.
Before (main): recording at 0:04, then airplane mode on. The recording bar disappears, the mic stops, and no error is shown. After reconnecting there is nothing to retry. The server never receives a transcribe request.
before.mp4
After, stopped while offline: recording continues through the drop (0:08). Tapping finish shows "Connection lost. Your recording is saved; retry once reconnected." After reconnecting, tapping the mic sends the saved recording. The server logs POST /api/voice/transcribe 200.
after.mp4
After, short blip: the connection drops at 0:03 and comes back. Recording continues (0:14), and finishing transcribes straight away.
blip.mp4
The emulator's microphone records silence (host audio input froze this emulator), so each run ends with "No speech was detected." rather than text. The server log shows the upload in each case.
Not checked: iOS. The code path is the same, but I only ran it on Android.
The voice route changes on every disconnect and reconnect, and the
provider cancelled the dictation on each change, deleting the audio.
Only a change of voice destination cancels now. The environment
transcriber looks up the current connection when it sends, so a
recording made across a reconnect can still be sent, and mobile keeps
a failed recording for retry, as web already does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Macroscope's review found this PR requires human approval — Dictation now survives reconnects and uses the current connection to the selected environment. Enabling failed-recording retention and retry for all mobile sessions changes the default audio lifecycle and requires human review.
You can add or adjust custom eligibility rules. Learn more.
Reviewing files that changed from the base of the PR and between ba9646b and 1c5effb.
You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.
Use the checkbox below for a quick retry:
Trigger review
No actionable comments were generated in the recent review.
Walkthrough
Voice transcription now resolves the current host connection during preparation and upload. If the host disconnects, the saved recording can be retried after reconnection. The provider cancels sessions on grant changes rather than route changes.
Changes: Voice input reconnect recovery
Layer / File(s)
Summary
Resolve connections for transcription apps/mobile/src/features/voice-input/voiceDestination.ts, apps/mobile/src/features/voice-input/voiceDestination.test.ts
The transcriber looks up the current connection during preparation and upload. If no connection is available during upload, it returns a saved-recording error. The test verifies that transcription succeeds after reconnection.
sequenceDiagram
participant VoiceInputSession
participant voiceRouteAtom
participant environmentTranscriber
participant HostConnection
VoiceInputSession->>voiceRouteAtom: Request transcription route
voiceRouteAtom->>environmentTranscriber: Provide current connection getter
environmentTranscriber->>HostConnection: Resolve connection for upload
HostConnection-->>environmentTranscriber: No connection
environmentTranscriber-->>VoiceInputSession: Return saved-recording transcription error
HostConnection->>HostConnection: Reconnect
VoiceInputSession->>environmentTranscriber: Retry saved recording
environmentTranscriber->>HostConnection: Resolve current connection
HostConnection-->>environmentTranscriber: Return connection
environmentTranscriber-->>VoiceInputSession: Return transcription text
Loading
Possibly related PRs
pingdotgg/t3code#33364: Changes related connection and grant checks for mobile voice input transcription.
Suggested labels:bugfix
Suggested reviewers:juliusmarminge, macroscopeapp
Merge Risk: Minimal · previous review ba964
This change keeps a dictation alive across a dropped connection and lets a saved recording be retried after reconnecting. No concrete merge-blocking risk was found.
Pre-merge checks: 4
Check name
Status
Explanation
Title check
The title names the main change: a dropped connection no longer discards a dictation. It follows the conventional commit form and matches the objectives and the file summaries, which show the grant-on…
Description check
The description follows the template. It has Problem, Change, Scope, and Verification sections, and each is filled in with specific detail. The Problem section gives reproduction steps. The Change sec…
Linked Issues check
Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check
Check skipped because no linked issues were found for this pull request.
Full details: Title check
Explanation
The title names the main change: a dropped connection no longer discards a dictation. It follows the conventional commit form and matches the objectives and the file summaries, which show the grant-only cancellation and the retry changes. The title is 64 characters, which is within the usual limit.
Full details: Description check
Explanation
The description follows the template. It has Problem, Change, Scope, and Verification sections, and each is filled in with specific detail. The Problem section gives reproduction steps. The Change section explains the fix and its scope. The Scope section explains why no issue is linked and states that the fix is a bug fix. The Verification section lists a focused test, Android emulator runs with before and after evidence, and what was not checked (iOS). The description does not link an issue, which the template asks for when one exists. The author gives a reason for omitting it. The model and harness are named at the end, as the template requests.
Finishing Touches
Generate unit tests (beta)
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.
Real Android app, Pixel 9 emulator, API 36, portrait 1080x2424, light theme. Fresh isolated state on each commit; identical synthetic project and environment-local Parakeet transcription model.
Confirm online dictation reaches the transcription endpoint.
Start dictation, enable airplane mode, then finish offline if the recorder survives; reconnect and retry.
Base received only the online control's transcription request. Head received successful HTTP 200 transcription responses for its control, offline retry, repeated offline capture, and reconnect-before-Finish flow. The microphone is silent, so No speech was detected is the expected end-to-end result. The composer exposes retry through its microphone button, labelled Start dictation; retry goes straight to Transcribing without starting another recording.
Videos were visually inspected. Both stills were captured after identical screenshot pairs separated by at least 500 ms.
Not shown:
Spoken-word recognition accuracy was not exercised because the emulator microphone is silent; retained-audio retry was verified through the real transcription endpoint and its No speech was detected result.
iOS and cloud-transcription destinations were not exercised; this proof covers Android with an environment-local speech model.
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
size:M30-99 changed lines (additions + deletions).takeover:needs-juliusTakeover done; route gate says Julius decidesvouch:unvouchedAuthor is not yet trusted in the VOUCHED list.
2 participants
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.
Problem
On mobile, a dictation sent to an environment is deleted when the connection drops. The recording stops, no error is shown, and after reconnecting there is nothing to retry, so the whole message has to be spoken again.
To reproduce: choose an environment for voice input in Settings → Voice, start dictating, then turn on airplane mode.
Change
The voice route changes whenever the connection drops or comes back, and
VoiceInputProvidercancelled the dictation on every route change. Cancelling mid-recording deletes the audio file without any message. A recording that survived anyway still could not be sent: its transcriber was tied to the connection object it was prepared on, and a reconnect replaces that object, so the send failed with "Your transcription choice changed."retryFailedRecording, as web already does. A failed send keeps the audio, and the existing retry (the mic button, or Retry in the voice control) sends it again without re-recording. Dismissing the error still deletes it.On-device transcription uses the live
startpath, so its recordings are not kept and its behaviour is unchanged. The one exception is Android builds without the live recorder, where a failed on-device transcription is now kept for retry, matching web.Scope
A bug fix. I hit it as a daily user: a dropped connection mid-dictation lost the whole message. I found no issue or open PR for it, and it needs none because losing a recording without a message is plainly unintended and the reproduction above takes a minute.
The change is in shared mobile code, so it covers iOS and Android. Web loses a dictation the same way:
useEnvironmentSpeechInputdisposes its controller whenever the connection object changes, and in streaming mode it keeps no local copy of the audio. That code is separate, so it is not changed here.Verification
Test. Added a case to
voiceDestination.test.ts: prepare a recording, drop the connection, send (it fails with the "saved" error), reconnect, send again (it transcribes). Onmainit fails withcode: "cancelled"("Your transcription choice changed."). With the fix it passes.Android emulator (API 35, arm64). Dev client built from this branch, an isolated dev server with the Canary 180M local model, and airplane mode to drop the connection. I drove the emulator with adb rather than T3's device panel, because agent device access was off in my session.
Before (
main): recording at 0:04, then airplane mode on. The recording bar disappears, the mic stops, and no error is shown. After reconnecting there is nothing to retry. The server never receives a transcribe request.before.mp4
After, stopped while offline: recording continues through the drop (0:08). Tapping finish shows "Connection lost. Your recording is saved; retry once reconnected." After reconnecting, tapping the mic sends the saved recording. The server logs
POST /api/voice/transcribe 200.after.mp4
After, short blip: the connection drops at 0:03 and comes back. Recording continues (0:14), and finishing transcribes straight away.
blip.mp4
The emulator's microphone records silence (host audio input froze this emulator), so each run ends with "No speech was detected." rather than text. The server log shows the upload in each case.
Not checked: iOS. The code path is the same, but I only ran it on Android.
Claude Opus 5.5 in Claude Code (T3 Code)
🤖 Generated with Claude Code