Skip to content

Parakeet can report “No model loaded” after auto-unload restore timeout #840

Description

@SeoFood

Summary

A user reports that Parakeet cold start works reliably when Auto-unload model is set to Never, but the same setup starts showing No model loaded again when Auto-unload is set back to the default timed policy.

This points to the restore path after an auto-unload, not initial setup/download.

User report

yes, after setting the auto-unload model to "never", the problem is solved. It is now performing cold start perfectly with no issues. However, as soon as I set the auto-unload model back to its default—which I believe is a few minutes—it starts exhibiting the same behavior. I think we need a fix for this.

Current behavior

When Parakeet has been unloaded by the auto-unload timer, the next dictation can fail with:

No model loaded. Please download and select a model first.

Setting auto-unload to Never avoids the issue.

Expected behavior

Auto-unload should release the local model from memory, but the next dictation should restore the selected/persisted Parakeet model before transcription starts. Users should not need to disable auto-unload to make cold starts reliable.

Investigation notes

Relevant code paths:

  • ModelAutoUnloadPolicy.defaultSeconds is 600, so the default policy is 10 minutes.
  • ModelManagerService.prepareEngineForTranscription(...) calls triggerRestoreModel(...) when the selected transcription plugin is not configured.
  • ModelManagerService.triggerRestoreModel(...) dispatches triggerRestoreModel through ObjC and polls plugin.isConfigured for up to ~30 seconds.
  • ParakeetPlugin.triggerAutoUnload() calls unloadModel(clearPersistence: false), preserving loadedModel.
  • ParakeetPlugin.triggerRestoreModel() starts Task { await restoreLoadedModel(allowDownloads: true) }.

Local checks performed:

xcodebuild test ...
Executed 3 focused auto-unload policy/timer tests, 0 failures

I also ran a temporary Parakeet plugin-level check locally: load Parakeet v3, unload with clearPersistence: false, then call triggerRestoreModel(). That passed, so the plugin-only reload path can work. However, the first Parakeet/CoreML load took about 29.7 seconds locally, which is very close to the host polling timeout. A slower machine, cold CoreML compile, or cleanup contention could plausibly hit the timeout and surface as No model loaded.

Possible root cause

The host restore wait may be too tight or too opaque for local Parakeet reload after auto-unload. If the async restore takes longer than the current polling window, ModelManagerService still sees plugin.isConfigured == false and throws modelNotLoaded, even though the persisted model selection is valid.

Acceptance criteria

  • Add regression coverage for: selected Parakeet/local transcription engine is auto-unloaded, next dictation restores it and proceeds instead of throwing modelNotLoaded.
  • Make the restore path resilient to slow first CoreML load/compile after auto-unload.
  • If restore fails for a real reason, surface the underlying load error instead of the generic No model loaded message.
  • Keep the Never behavior unchanged.

Workaround

Set Auto-unload model to Never until this path is fixed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: dictationRelated to recording and transcriptionarea: pluginsPlugin systembugSomething isn't working

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions