{"record":{"id":"1f66a202d7117123","repo":"cjpais/Handy","slug":"failed-to-register-transcription-signal-handlers","errorCode":null,"errorMessage":"failed to register transcription signal handlers","messagePattern":"failed to register transcription signal handlers","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src-tauri/src/signal_handle.rs","lineNumber":38,"sourceCode":"        c.send_external_input(binding_id, source);\n    } else {\n        warn!(\"TranscriptionCoordinator not initialized\");\n    }\n}\n\n/// Listen for Unix signals that remotely toggle transcription.\n///\n/// SIGUSR2 toggles plain transcription on all Unix platforms. SIGUSR1\n/// (transcription with post-processing) is only handled on macOS: on Linux,\n/// WebKitGTK's JavaScriptCore garbage collector sends SIGUSR1 to its own\n/// threads to suspend them, so handling it caused phantom recordings on every\n/// GC cycle (#1660). Linux users should use `handy --toggle-post-process`\n/// instead.\n#[cfg(unix)]\npub fn setup_signal_handler(app_handle: AppHandle) {\n    #[cfg(target_os = \"macos\")]\n    let mut signals =\n        Signals::new([SIGUSR1, SIGUSR2]).expect(\"failed to register transcription signal handlers\");\n    #[cfg(not(target_os = \"macos\"))]\n    let mut signals =\n        Signals::new([SIGUSR2]).expect(\"failed to register transcription signal handlers\");\n    #[cfg(target_os = \"macos\")]\n    debug!(\"Signal handlers registered (SIGUSR1, SIGUSR2)\");\n    #[cfg(not(target_os = \"macos\"))]\n    debug!(\"Signal handler registered (SIGUSR2; SIGUSR1 is left to WebKitGTK)\");\n    thread::spawn(move || {\n        for sig in signals.forever() {\n            let (binding_id, signal_name) = match sig {\n                #[cfg(target_os = \"macos\")]\n                SIGUSR1 => (\"transcribe_with_post_process\", \"SIGUSR1\"),\n                SIGUSR2 => (\"transcribe\", \"SIGUSR2\"),\n                _ => continue,\n            };\n            debug!(\"Received {signal_name}\");\n            send_transcription_input(&app_handle, binding_id, signal_name);\n        }","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/cjpais/Handy/blob/c6fa60da2f13a5af660fba17f37af548855119c5/src-tauri/src/signal_handle.rs#L20-L56","documentation":"Panic on macOS when signal-hook's Signals::new cannot install handlers for SIGUSR1/SIGUSR2. Registration allocates the signal set and installs dispositions; it fails when the process cannot manage signals — another component already owning these user signals incompatibly, sigaction/kqueue registration errors (e.g. EMFILE), or restricted runtimes (sandboxes, some test harnesses) that block signal management.","triggerScenarios":"setup_signal_handler at startup on macOS: Signals::new([SIGUSR1, SIGUSR2]) returns Err because a prior in-process handler claims one of the signals incompatibly, or registration fails under sandbox/resource constraints.","commonSituations":"Embedding Handy's core in another app or test runner that also installs SIGUSR handlers; running under debuggers/profilers that intercept user signals; forks that re-register handlers or call setup twice.","solutions":["Identify the other owner of SIGUSR1/SIGUSR2 in the process and let exactly one component register them","Degrade gracefully instead of expecting: log a warning and continue — remote control still works via handy --toggle-transcription through the single-instance channel","If a sandbox policy blocks signal management, allow user signals or run without the signal feature"],"exampleFix":"// before\n#[cfg(target_os = \"macos\")]\nlet mut signals =\n    Signals::new([SIGUSR1, SIGUSR2]).expect(\"failed to register transcription signal handlers\");\n\n// after — signals are an optional remote control; skip them on failure\n#[cfg(target_os = \"macos\")]\nlet mut signals = match Signals::new([SIGUSR1, SIGUSR2]) {\n    Ok(s) => s,\n    Err(e) => {\n        log::warn!(\"signal handlers disabled: {e}\");\n        return;\n    }\n};","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Register signal handlers exactly once at process start and make registration non-fatal (log and continue)","When embedding Handy's core in another app or test harness, let the host own user signals and rely on the CLI remote control instead","Remember signals are an optional remote control; handy --toggle-transcription via single-instance always works without them"],"tags":["rust","signal-hook","macos","unix","signals"],"backgroundTag":"signal-handler-registration-failed","analyzedSha":"c6fa60da2f13a5af660fba17f37af548855119c5","analyzedAt":"2026-08-17T10:29:55.597Z","contentChangedAt":"2026-08-17T10:29:55.597Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}