{"record":{"id":"6e4f20bf6b88d25a","repo":"mfontanini/presenterm","slug":"failed-to-create-speaker-notes-listener-e","errorCode":null,"errorMessage":"failed to create speaker notes listener: {e}","messagePattern":"failed to create speaker notes listener: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/main.rs","lineNumber":377,"sourceCode":"    events_publisher: Option<SpeakerNotesEventPublisher>,\n}\n\nimpl SpeakerNotesComponents {\n    fn new(cli: &Cli, config: &Config, path: &Path) -> anyhow::Result<Self> {\n        let full_presentation_path = path.canonicalize().unwrap_or_else(|_| path.to_path_buf());\n        let publish_speaker_notes =\n            cli.publish_speaker_notes || (config.speaker_notes.always_publish && !cli.listen_speaker_notes);\n        let events_publisher = publish_speaker_notes\n            .then(|| {\n                SpeakerNotesEventPublisher::new(config.speaker_notes.publish_address, full_presentation_path.clone())\n            })\n            .transpose()\n            .map_err(|e| anyhow!(\"failed to create speaker notes publisher: {e}\"))?;\n        let events_listener = cli\n            .listen_speaker_notes\n            .then(|| SpeakerNotesEventListener::new(config.speaker_notes.listen_address, full_presentation_path))\n            .transpose()\n            .map_err(|e| anyhow!(\"failed to create speaker notes listener: {e}\"))?;\n        Ok(Self { events_listener, events_publisher })\n    }\n}\n\nfn overflow_validation_enabled(mode: &PresentMode, config: &ValidateOverflows) -> bool {\n    match (config, mode) {\n        (ValidateOverflows::Always, _) => true,\n        (ValidateOverflows::Never, _) => false,\n        (ValidateOverflows::WhenPresenting, PresentMode::Presentation) => true,\n        (ValidateOverflows::WhenDeveloping, PresentMode::Development) => true,\n        _ => false,\n    }\n}\n\nfn run(cli: Cli) -> Result<(), Box<dyn std::error::Error>> {\n    #[cfg(feature = \"json-schema\")]\n    if cli.generate_config_file_schema {\n        let schema = schemars::schema_for!(Config);","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/mfontanini/presenterm/blob/5f8add11a24af9d257fd18da48c8004dd9c516f8/src/main.rs#L359-L395","documentation":"This error is raised in `SpeakerNotesComponents::new` when `SpeakerNotesEventListener::new` returns an `io::Error`. The listener constructor creates a UDP (DGRAM) socket, sets SO_REUSEADDR and non-blocking mode, then binds to the configured listen address; any failing step is wrapped with this context. It means the speaker-notes listener could not claim its UDP port, so `--listen-speaker-notes` mode cannot start.","triggerScenarios":"The CLI was started with `--listen-speaker-notes` and `SpeakerNotesEventListener::new(config.speaker_notes.listen_address, ...)` fails. Most common: `s.bind(&address)` fails with EADDRINUSE because another process (or the OS denying reuse on macOS where SO_REUSEADDR is not set) already holds the listen port; also `Socket::new` failing (no UDP socket support / fd exhaustion) or `set_reuse_address`/`set_nonblocking` returning an error.","commonSituations":"Another presenterm instance (or any UDP service) is already bound to the listen address, so the port is taken; the configured `speaker_notes.listen_address` is a privileged port (<1024) while running unprivileged (EACCES); running on macOS where the code skips SO_REUSEADDR and a stale socket still holds the port; sandboxed environments blocking socket creation.","solutions":["Check what holds the listen port (e.g. `ss -ulpen` or `lsof -i :<port>`) and stop that process, or pick a different `speaker_notes.listen_address` port in your config.","If binding a privileged port, use a port above 1024 or run with sufficient privileges.","On macOS, note SO_REUSEADDR is not applied: ensure no previous listener instance is still running on the port.","Raise the file-descriptor limit if `Socket::new` fails with 'Too many open files', and verify the environment permits UDP sockets."],"exampleFix":"// before (config.toml) - port already used by another listener\n[speaker_notes]\nlisten_address = \"127.0.0.1:48700\"\n\n// after - choose a free port\n[speaker_notes]\nlisten_address = \"127.0.0.1:48701\"","handlingStrategy":"validation","validationCode":"// Before launching with --listen-speaker-notes, check the port is free:\nuse std::net::UdpSocket;\nfn validate_listen_address(addr: std::net::SocketAddr) -> Result<(), String> {\n    match UdpSocket::bind(addr) {\n        Ok(_) => Ok(()),\n        Err(e) => Err(format!(\"listen address {addr} unusable: {e} (in use or privileged?)\")),\n    }\n}","typeGuard":null,"tryCatchPattern":"// Distinguish bind conflicts and fail with a clear message:\nmatch SpeakerNotesComponents::new(...) {\n    Ok(components) => components,\n    Err(e) if e.to_string().contains(\"speaker notes listener\") => {\n        eprintln!(\"listener setup failed: {e:#}; is another speaker-notes listener on this port?\");\n        std::process::exit(1);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Pick an unprivileged, high-numbered UDP port for listen_address.","Ensure only one speaker-notes listener runs per host on non-macOS, and none concurrently on macOS (no SO_REUSEADDR there).","Check port usage with `ss -ulpen` / `lsof -i :<port>` before starting.","Use distinct port pairs for publish/listen in shared config files across machines."],"tags":["network","udp","socket","address-in-use","rust"],"backgroundTag":"address-already-in-use","analyzedSha":"5f8add11a24af9d257fd18da48c8004dd9c516f8","analyzedAt":"2026-09-12T11:12:04.627Z","contentChangedAt":"2026-09-12T11:12:04.627Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}