{"record":{"id":"fe24f31a03c8c0b7","repo":"mfontanini/presenterm","slug":"failed-to-create-speaker-notes-publisher-e","errorCode":null,"errorMessage":"failed to create speaker notes publisher: {e}","messagePattern":"failed to create speaker notes publisher: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"src/main.rs","lineNumber":372,"sourceCode":"    }\n}\n\nstruct SpeakerNotesComponents {\n    events_listener: Option<SpeakerNotesEventListener>,\n    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}","sourceCodeStart":354,"sourceCodeEnd":390,"githubUrl":"https://github.com/mfontanini/presenterm/blob/5f8add11a24af9d257fd18da48c8004dd9c516f8/src/main.rs#L354-L390","documentation":"This error is raised in `SpeakerNotesComponents::new` when `SpeakerNotesEventPublisher::new` returns an `io::Error`. The publisher constructor binds a UDP socket to 127.0.0.1:0, enables broadcast, and connects it to the configured publish address; any of those OS-level socket operations failing is wrapped in this anyhow context. It means presenterm could not set up the speaker-notes UDP publisher and the presentation cannot start with publishing enabled.","triggerScenarios":"The CLI was started with `--publish-speaker-notes` (or the config has `speaker_notes.always_publish` true and `--listen-speaker-notes` is not set), and `SpeakerNotesEventPublisher::new(config.speaker_notes.publish_address, ...)` fails: the UDP bind to 127.0.0.1:0 fails (no ephemeral ports available, socket resource limits), `set_broadcast` is rejected, or `connect` to the publish address fails (invalid/unroutable address family, e.g. an IPv6 publish address against the IPv4-bound socket, or network-unreachable).","commonSituations":"Misconfigured `speaker_notes.publish_address` in the config (wrong port, IPv6 address, or malformed SocketAddr); running in a sandbox/container that blocks socket creation (seccomp, no network namespace); hitting the OS file-descriptor limit so bind fails with EMFILE; running on a host without a loopback interface.","solutions":["Check the `speaker_notes.publish_address` in your config (or the relevant CLI flag) is a valid IPv4 SocketAddr, e.g. the default 127.0.0.1 style address with a free port.","Run `ulimit -n` and raise the file-descriptor limit if bind fails with 'Too many open files'.","If running in a container/sandbox, allow loopback UDP socket creation (don't use network isolation that strips loopback).","Use the raw error after the colon (the io::Error message) to identify whether bind, set_broadcast, or connect failed, and address that specific cause."],"exampleFix":"// before (typo'd / invalid publish address in config.toml)\n[speaker_notes]\npublish_address = \"localhost:48700\"  // or an IPv6 address\n\n// after\n[speaker_notes]\npublish_address = \"127.0.0.1:48700\"","handlingStrategy":"validation","validationCode":"// Before launching with --publish-speaker-notes, verify the publish address is bindable/reachable:\nuse std::net::{UdpSocket, SocketAddr};\nfn validate_publish_address(addr: SocketAddr) -> Result<(), String> {\n    let s = UdpSocket::bind(\"127.0.0.1:0\")\n        .map_err(|e| format!(\"cannot bind ephemeral port: {e}\"))?;\n    s.connect(addr)\n        .map_err(|e| format!(\"cannot connect to publish address {addr}: {e}\"))\n}","typeGuard":null,"tryCatchPattern":"// In Rust the error is already context-wrapped; match on the io cause:\nmatch SpeakerNotesComponents::new(...) {\n    Ok(components) => components,\n    Err(e) if e.to_string().contains(\"speaker notes publisher\") => {\n        eprintln!(\"publisher setup failed: {e:#}; check speaker_notes.publish_address\");\n        std::process::exit(1);\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Keep publish_address an IPv4 loopback SocketAddr (e.g. 127.0.0.1:<port>) as the defaults intend.","Raise the nofile ulimit in environments that open many sockets.","Test publisher/listener setup in containers and sandboxes before presenting.","Don't disable loopback networking where presenterm runs."],"tags":["network","udp","socket","configuration","rust"],"backgroundTag":"connection-refused","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"}