Platform
macOS
Problem / Motivation
When a cloud transcription fails, TypeWhisper preserves the audio as a recovery WAV and makes it reachable in two places: a Recover Last Recording item in the menu bar menu, and Settings > Recovery. What is missing is anything at the moment of failure that points at either one.
Seen on macOS 26.4.1 (Apple Silicon), TypeWhisper 1.6.0 (1091).
I lost a dictation to an upstream HTTP 522 (the error message content is #1266). The recovery was preserved correctly and promptly, about 30 ms after the failing response arrived, correctly named and in the right directory. The failure banner appeared as designed, showed the error text for three seconds, and said nothing about a recovery having been saved. I went to Settings > Recovery, did not come away with the recording in hand, and ended up in File Transcription selecting the WAV by hand, which is the manual version of what the app had already prepared.
I am deliberately not making a claim about what the Recovery pane displayed at that moment, because I cannot establish it after the fact. This report does not depend on it. Whatever the pane showed, nothing at the moment of failure said a recovery existed, and that is the gap.
The problem is discoverability at the one moment it matters. The menu item is only visible after opening the menu bar menu, and the menu bar icon itself does not change when a recovery exists, since MenuBarExtraLabel reflects recording state alone. Settings > Recovery requires knowing the pane is there. The one surface that fires unprompted, the failure banner, carries no recovery information: showError passes error.localizedDescription through unchanged, and none of the three indicator views mentions recovery.
What I verified in the code before filing, so this is not a report about a broken pipeline. These are static checks, not observations of a running app:
DictationRecoveryAudioStore's discovery predicate accepts the generated filename.
- The default retention policy keeps it:
dictationRecoveryRetentionDays unset resolves to 30 days.
preserveActiveRecoveryRecording publishes into both recoverableRecordingURLs and recoverableRecordingURL, so both the Settings pane and the menu bar item have an input.
DictationRecoveryView.onAppear refreshes, and DictationRecoveryViewModel also subscribes to the published list.
- The pane offers a one-click retry with engine, model, task and language pickers.
So the preserve-and-publish path is wired. The user is simply never told at the moment the recovery is created.
There is a second instance of the same shape: the "No speech recognized" path also preserves a recovery and also says nothing about it.
Proposed Solution
Let the failure banner that already fires carry the recovery.
This is already half-done in an open PR. #1246 ("Bound the final-transcription phase with a deadline") adds "Transcription timed out after N seconds. The recording was kept in Dictation Recovery." to the banner for the deadline error class. That is exactly the right idea; it just does not reach the general failure path, adds no action, and touches no indicator view. What I am asking for is that behaviour generalized.
Two things make this smaller than it sounds:
showNotchFeedback already takes an undoTitle, and all three indicator views already render an action gated on actionFeedbackUndoTitle != nil. showError passes nil. So this is populating an existing parameter rather than building a new affordance. The one real change is that onAction is currently hardcoded to undoActionFeedback() and would need generalizing.
- The navigation already exists:
recoverLastRecording() opens Settings > Recovery.
So: when a dictation fails and a recovery was preserved, mention it in the banner and give it a single action that opens Settings > Recovery. It should not auto-submit, since the upstream cause may still be failing, and it should appear only when a recovery actually exists. The undo banner already uses duration: 12.0 against showError's 3.0, so there is in-repo precedent for giving an actionable banner longer to live.
Alternatives Considered
- A system notification. Heavier, needs notification permission, and is easy to miss or to have turned off.
- Changing the menu bar icon when a recovery exists. Persistent rather than transient, so it would nag until dismissed, and the icon currently carries recording state only.
- Leaving it as is and documenting the menu item. Cheapest, but the moment a user decides the audio is gone is the moment of failure, which is before they would go looking.
One smaller, separate observation: the dictation-recovery store emits no log lines, so a preserve that happened and one that never happened look identical afterwards. Across three unified-log captures of the incident window I measured 0 lines matching "recover" against TypeWhisper control counts of 23,642, 6,475 and 134,705. I am happy to open a PR adding counts at the preserve, publish and retention points if that would be welcome.
Platform
macOS
Problem / Motivation
When a cloud transcription fails, TypeWhisper preserves the audio as a recovery WAV and makes it reachable in two places: a
Recover Last Recordingitem in the menu bar menu, and Settings > Recovery. What is missing is anything at the moment of failure that points at either one.Seen on macOS 26.4.1 (Apple Silicon), TypeWhisper 1.6.0 (1091).
I lost a dictation to an upstream HTTP 522 (the error message content is #1266). The recovery was preserved correctly and promptly, about 30 ms after the failing response arrived, correctly named and in the right directory. The failure banner appeared as designed, showed the error text for three seconds, and said nothing about a recovery having been saved. I went to Settings > Recovery, did not come away with the recording in hand, and ended up in File Transcription selecting the WAV by hand, which is the manual version of what the app had already prepared.
I am deliberately not making a claim about what the Recovery pane displayed at that moment, because I cannot establish it after the fact. This report does not depend on it. Whatever the pane showed, nothing at the moment of failure said a recovery existed, and that is the gap.
The problem is discoverability at the one moment it matters. The menu item is only visible after opening the menu bar menu, and the menu bar icon itself does not change when a recovery exists, since
MenuBarExtraLabelreflects recording state alone. Settings > Recovery requires knowing the pane is there. The one surface that fires unprompted, the failure banner, carries no recovery information:showErrorpasseserror.localizedDescriptionthrough unchanged, and none of the three indicator views mentions recovery.What I verified in the code before filing, so this is not a report about a broken pipeline. These are static checks, not observations of a running app:
DictationRecoveryAudioStore's discovery predicate accepts the generated filename.dictationRecoveryRetentionDaysunset resolves to 30 days.preserveActiveRecoveryRecordingpublishes into bothrecoverableRecordingURLsandrecoverableRecordingURL, so both the Settings pane and the menu bar item have an input.DictationRecoveryView.onAppearrefreshes, andDictationRecoveryViewModelalso subscribes to the published list.So the preserve-and-publish path is wired. The user is simply never told at the moment the recovery is created.
There is a second instance of the same shape: the "No speech recognized" path also preserves a recovery and also says nothing about it.
Proposed Solution
Let the failure banner that already fires carry the recovery.
This is already half-done in an open PR. #1246 ("Bound the final-transcription phase with a deadline") adds
"Transcription timed out after N seconds. The recording was kept in Dictation Recovery."to the banner for the deadline error class. That is exactly the right idea; it just does not reach the general failure path, adds no action, and touches no indicator view. What I am asking for is that behaviour generalized.Two things make this smaller than it sounds:
showNotchFeedbackalready takes anundoTitle, and all three indicator views already render an action gated onactionFeedbackUndoTitle != nil.showErrorpassesnil. So this is populating an existing parameter rather than building a new affordance. The one real change is thatonActionis currently hardcoded toundoActionFeedback()and would need generalizing.recoverLastRecording()opens Settings > Recovery.So: when a dictation fails and a recovery was preserved, mention it in the banner and give it a single action that opens Settings > Recovery. It should not auto-submit, since the upstream cause may still be failing, and it should appear only when a recovery actually exists. The undo banner already uses
duration: 12.0againstshowError's3.0, so there is in-repo precedent for giving an actionable banner longer to live.Alternatives Considered
One smaller, separate observation: the dictation-recovery store emits no log lines, so a preserve that happened and one that never happened look identical afterwards. Across three unified-log captures of the incident window I measured 0 lines matching "recover" against TypeWhisper control counts of 23,642, 6,475 and 134,705. I am happy to open a PR adding counts at the preserve, publish and retention points if that would be welcome.