Merge remote-tracking branch 'origin/main' into fix/1856-dictation-step

# Conflicts:
#	CHANGELOG.md
This commit is contained in:
Palash Debnath
2026-09-09 14:27:12 -07:00
3 changed files with 79 additions and 0 deletions
+1
View File
@@ -10,6 +10,7 @@ the frozen-backend fallback mirror it for their toolchains.
**Highlights**
- The last onboarding step offers to install a speech-to-text model instead of failing three times when none is installed (#1856)
- A download that fails because the folder sits behind a mount point Windows will not cross now says so, and where to move it (#1957)
- A GPU that is merely short on free memory is no longer told to reinstall its drivers (#1812) — thanks @michaelhuamanflores!
- An error thrown by a browser extension is filtered on Safari and the macOS app too, not only on Chromium (#1901) — thanks @Chang-Jin-Lee!
- Choosing the China mirror no longer re-races the network on every dependency step, which cost seconds per step on blocked connections (#1892) — thanks @yuezheng2006!
+11
View File
@@ -97,6 +97,7 @@ _HINTS: dict[str, str] = {
"TRANSFORMERS_IMPORT": "Your transformers install is incomplete, or a package it loads models through (torchaudio, torchvision) is missing or mismatched with your torch — a torch/torchvision version mismatch fails with exactly this wording. Reinstall them together at the pinned versions (`uv pip install --python .venv --reinstall torch==2.8.0 torchaudio==2.8.0 torchvision==0.23.0 transformers` in the project folder), then restart the backend. If only transcription is affected, switching ASR to faster-whisper (Model Catalogue → Models) also works around it.",
"WINDOWS_APP_CONTROL_BLOCKED": "Windows refused to load a file VoiceStudio needs — an Application Control policy (Smart App Control, WDAC, or AppLocker) blocked it. On a personal PC: Windows Security → App & browser control → Smart App Control → Off (Windows only lets you turn it off once — re-enabling requires a Windows reset), then restart VoiceStudio. On a managed/work PC, ask IT to allow the VoiceStudio install folder.",
"WINDOWS_PAGING_FILE_TOO_SMALL": "Windows ran out of virtual memory while mapping the model into memory — its paging file is smaller than the model needs. This is not the same as your RAM being full, and closing other apps usually won't fix it: Windows has to be allowed to back the mapping. Set a bigger paging file — Settings → System → About → Advanced system settings → Performance → Settings → Advanced → Virtual memory → Change: untick \"Automatically manage\", pick your system drive, choose \"Custom size\" and set both Initial and Maximum to at least 32768 MB (more than the model's size), then OK and restart Windows. A smaller/quantized engine (OmniVoice GGUF, Supertonic-3) also avoids the large mapping entirely.",
"WINDOWS_UNTRUSTED_MOUNT": "Windows refused to walk a folder on the way to this file because the path crosses a mount point it does not trust (WinError 448). That is a Windows rule about the VOLUME, not about VoiceStudio or the file itself — it turns up on Dev Drives, on mounted VHD/ReFS volumes, and on junctions pointing into another user profile, so retrying the same link cannot help. Point VoiceStudio at a folder on an ordinary local drive instead: Settings → Storage → data directory, or the download/output folder named in the message. If that folder has to stay where it is, trust the volume with `fsutil devdrv trust <drive>:` from an elevated prompt and restart.",
"MEDIA_TOOL_MISSING": "VoiceStudio's media engine (ffmpeg/ffprobe) wasn't on the system path when a component went looking for it. Open Settings → Audio tools and use Download/Repair to fetch the bundled copy, then retry — a restart picks it up for everything. If you'd rather use a system install, install ffmpeg (macOS: `brew install ffmpeg`; Windows: `winget install Gyan.FFmpeg`; Linux: your package manager) and restart VoiceStudio, or point FFMPEG_PATH / OMNIVOICE_FFPROBE_PATH at the binaries in Settings.",
"AUDIO_IO_FAILED": "An audio file couldn't be read or written at the OS level. Check the drive isn't full, that the output and temp folders exist and are writable, and that antivirus or OneDrive isn't locking them (add a VoiceStudio exclusion if you use one).",
"VIDEO_DOWNLOAD_OS_ERROR": "The OS refused a file operation while saving the downloaded video — this is a disk/folder problem, not a network one, so retrying the same link won't help. The download is written to a job folder under your VoiceStudio data directory (Settings → Storage shows the path): check that drive isn't full, that the folder exists and is writable, and that antivirus or a cloud-sync client (OneDrive, Dropbox) isn't locking it — add a VoiceStudio exclusion if you use one. If your data directory sits on a synced or network drive, move it to a local one.",
@@ -308,6 +309,10 @@ _CONTEXT_FREE_HINT_CLASSES = frozenset({
# a Windows virtual-memory setting rather than a connectivity problem, and
# the detailed hint we already had for it never reached them.
"WINDOWS_PAGING_FILE_TOO_SMALL",
# #1957: triggered by WinError 448 or the literal "untrusted mount
# point" — both unmistakable, and it reaches the user as a bare
# download failure with only the OS sentence attached.
"WINDOWS_UNTRUSTED_MOUNT",
# Its trigger is a VoiceStudio-authored sentence — "the TTS model cache
# for … is incomplete" plus "could not be auto-repaired" / "weights
# missing" — so it cannot be produced by an unrelated library. The 500
@@ -576,6 +581,12 @@ def classify(reason: str) -> str:
or "application control policy" in low
):
return "WINDOWS_APP_CONTROL_BLOCKED"
# #1957: the path to a download or output file crosses a mount point
# Windows will not traverse (Dev Drive, mounted VHD/ReFS, a junction into
# another profile). Matched on the numeric code first because the OS
# translates the sentence, with the English phrase as a fallback.
if "[winerror 448]" in low or "untrusted mount point" in low:
return "WINDOWS_UNTRUSTED_MOUNT"
# #1221: libsndfile failed an OS-level audio read/write. Its own wording is
# a bare "System error.", so match the library name — audio_io already
# prefixes the target path and free space onto the write-path failures.
+67
View File
@@ -0,0 +1,67 @@
"""#1957 — WinError 448 must arrive with a remedy, not as a bare OS sentence.
A download failed with "[WinError 448] The path cannot be traversed because it
contains an untrusted mount point: <a folder on D:>" and nothing else.
That is a Windows rule about the VOLUME — Dev Drives, mounted VHD/ReFS volumes,
and junctions into another profile all trigger it — so no amount of retrying
the same link helps, and the message gives the user nothing to change.
Matched on the numeric code first because Windows translates the sentence, with
the English phrase as a fallback for locales that report it verbatim.
"""
import pytest
from core.failure import _CONTEXT_FREE_HINT_CLASSES, classify, public_hint_for_topic
from core.public_errors import public_exception_response
_RAW = (
"download: [WinError 448] The path cannot be traversed because it contains "
r"an untrusted mount point: 'D:\CodingStuff\nodejs'"
)
def test_the_windows_code_is_classified():
assert classify(_RAW) == "WINDOWS_UNTRUSTED_MOUNT"
def test_a_translated_message_still_classifies_on_the_code():
# Windows localises the sentence; the bracketed code is what survives.
assert classify("[WinError 448] Der Pfad kann nicht durchlaufen werden") == (
"WINDOWS_UNTRUSTED_MOUNT"
)
def test_the_english_phrase_alone_classifies():
assert classify("contains an untrusted mount point") == "WINDOWS_UNTRUSTED_MOUNT"
def test_the_hint_names_something_the_user_can_change():
hint = public_hint_for_topic("WINDOWS_UNTRUSTED_MOUNT")
assert "Storage" in hint
assert "448" in hint
def test_it_reaches_the_user_on_a_context_free_surface():
# The failure arrives through the global 500 handler, which only attaches
# hints from the allowlist — so being classified is not enough on its own.
assert "WINDOWS_UNTRUSTED_MOUNT" in _CONTEXT_FREE_HINT_CLASSES
payload = public_exception_response(OSError(_RAW), fallback="Internal error.")
assert payload["docs_topic"] == "WINDOWS_UNTRUSTED_MOUNT"
assert payload["detail"] != "Internal error."
def test_the_offending_path_is_never_echoed_back():
payload = public_exception_response(OSError(_RAW), fallback="Internal error.")
for value in payload.values():
assert "CodingStuff" not in str(value)
@pytest.mark.parametrize(
"other",
[
"[WinError 1260] blocked by an application control policy",
"[WinError 1455] The paging file is too small for this operation",
],
)
def test_the_other_windows_classes_are_untouched(other):
assert classify(other) != "WINDOWS_UNTRUSTED_MOUNT"