Improve error handling for missing native binaries - #2
Conversation
check_dependencies() now checks if the record binary exists directly instead of calling ensure_swift_binary(), which attempts recompilation on every call. In packaged builds where source .swift files don't ship, this caused hundreds of WARN-level log entries every few seconds from the onboarding status poller. Also downgrade the "Source not found" log in ensure_swift_binary from warn to debug — it's expected in packaged builds and not actionable. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
build_binary() now verifies git and cmake are installed before cloning or configuring. Without this, a missing cmake causes a confusing "cmake failed: No such file or directory" that the error mapper rewrites to the generic "Whisper is not set up" — hiding the actual fix. The new error messages intentionally avoid the word "whisper" so they pass through to_user_facing_error() verbatim, giving the user an actionable install command. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ages When the record binary doesn't exist, the error mapper was showing "Grant microphone access..." which is misleading — the real problem is a missing binary, not a permission issue. Now errors containing "unavailable", "not found", or "command not found" correctly say "Audio recorder binary is missing" with an actionable fix. Permission- related errors still point to System Settings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
doramirdor
left a comment
There was a problem hiding this comment.
Verified on the branch: cargo test passes (147), no new clippy warnings. The shape of all three fixes is right — build_binary checks after the bin.exists() early return so there's no cost on the happy path, start() still compiles on demand so dropping ensure_swift_binary from check_dependencies doesn't break the dev flow, and both new error branches are covered by tests.
One blocker plus two minor notes inline.
Blocker: cmd_exists inherits the app's bare PATH, so the new pre-check will reject Homebrew cmake on machines where the build currently succeeds.
Minor: the most likely missing-binary spawn error still routes to the microphone-permission message, and check_dependencies now reports false during the first-launch compile window.
🤖 Generated with Claude Code
| Ok(()) | ||
| } | ||
|
|
||
| fn cmd_exists(name: &str) -> bool { |
There was a problem hiding this comment.
Blocker — this ignores the PATH augmentation that every other subprocess spawn in the codebase does, so the new guard will falsely block working builds.
run_cmd directly below adds /opt/homebrew/bin:/usr/local/bin (same in parakeet.rs:283, refinement/cli.rs:10, codebase/analyzer.rs:62) precisely because a Finder-launched .app inherits /usr/bin:/bin:/usr/sbin:/sbin. cmd_exists inherits that bare PATH, so a Homebrew cmake at /opt/homebrew/bin/cmake is invisible and the check returns "cmake is not installed" on a machine where run_cmd would have found it fine. That's a regression, and it lands on exactly the packaged users this PR targets.
Hoist the PATH the file already builds:
fn build_path_env() -> String {
format!("{}:/opt/homebrew/bin:/usr/local/bin", std::env::var("PATH").unwrap_or_default())
}
fn cmd_exists(name: &str) -> bool {
Command::new(name)
.arg("--version")
.env("PATH", build_path_env())
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}…and have run_cmd use build_path_env() too.
Nit while here: on a Mac without CLT, cmd_exists("git") runs the /usr/bin/git shim, which pops the system "install command line tools" dialog as a side effect of the check. Probably fine — arguably helpful — just worth knowing it isn't a silent probe.
|
|
||
| if lower.contains("audio recorder") || lower.contains("record.swift") || lower.contains("rec: command not found") { | ||
| return "Audio recording is unavailable. Grant microphone access in System Settings → Privacy & Security → Microphone (and install Xcode Command Line Tools if prompted).".into(); | ||
| if lower.contains("unavailable") || lower.contains("not found") || lower.contains("command not found") { |
There was a problem hiding this comment.
Minor — this misses the most likely missing-binary error, so the case the PR is fixing survives on the main path.
recorder.rs:71 produces Failed to start native audio recorder: No such file or directory (os error 2) when the spawn fails. That contains audio recorder but none of unavailable / not found / command not found, so it falls through to the permission message — the exact "Grant microphone access" mislabel this PR is removing.
if lower.contains("unavailable") || lower.contains("not found")
|| lower.contains("command not found") || lower.contains("no such file")
{|
|
||
| pub fn check_dependencies() -> (bool, String) { | ||
| if crate::utils::swift_binary::ensure_swift_binary("record", "scripts/record.swift") { | ||
| if crate::utils::swift_binary::get_binary_path("record").exists() { |
There was a problem hiding this comment.
Minor / FYI — right call for the log spam, but note the behavior change: check_dependencies no longer compiles, so on first launch it reports false for the window while ensure_swift_binary_async (lib.rs:2203) is still building the helper. Transient false negative in the health panel / tray until the async compile lands. Acceptable as-is, just shouldn't be a surprise later.
…rror mapper cmd_exists now uses the same Homebrew-aware PATH as run_cmd so the pre-build check for git/cmake doesn't false-reject on Finder-launched .app bundles. The error mapper now matches "no such file" so a missing recorder binary spawn failure routes to the correct user-facing message instead of the microphone-permission one. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
When Echo is installed without Xcode Command Line Tools or cmake, several things go wrong silently or with misleading error messages. This PR fixes three related issues:
check_dependencies()was callingensure_swift_binary()on every status poll (~every 3s), logging a WARN each time in packaged builds where source.swiftfiles don't ship. Now it just checks if the binary exists. Also downgraded the log todebug!.build_binary()now verifiesgitandcmakeare installed before cloning or configuring whisper.cpp, with actionable error messages (e.g. "Install it with:brew install cmake") instead of a generic "Whisper is not set up."recordbinary doesn't exist, the error mapper was showing "Grant microphone access..." which is wrong. Now missing-binary errors say "Audio recorder binary is missing. Reinstall Echo or install Xcode Command Line Tools" while permission errors still point to System Settings.Rust-only changes — no TypeScript/renderer changes.
Test plan
cargo testpasses (147 tests, including 2 new error-mapping tests)🤖 Generated with Claude Code