{"record":{"id":"3d65c16aed00b6dc","repo":"gleam-lang/gleam","slug":"error-setting-ctrl-c-handler","errorCode":null,"errorMessage":"Error setting Ctrl-C handler","messagePattern":"Error setting Ctrl-C handler","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"compiler-cli/src/run.rs","lineNumber":39,"sourceCode":"\n#[derive(Debug, Clone, Copy)]\npub enum Which {\n    Src,\n    Test,\n    Dev,\n}\n\npub fn command(\n    paths: &ProjectPaths,\n    arguments: Vec<String>,\n    target: Option<Target>,\n    runtime: Option<Runtime>,\n    module: Option<String>,\n    which: Which,\n    no_print_progress: bool,\n) -> Result<(), Error> {\n    // Don't exit on ctrl+c as it is used by child erlang shell\n    ctrlc::set_handler(move || {}).expect(\"Error setting Ctrl-C handler\");\n    let command = setup(\n        paths,\n        arguments,\n        target,\n        runtime,\n        module,\n        which,\n        no_print_progress,\n    )?;\n    let status = ProjectIO::new().exec(command)?;\n    std::process::exit(status);\n}\n\npub fn setup(\n    paths: &ProjectPaths,\n    arguments: Vec<String>,\n    target: Option<Target>,\n    runtime: Option<Runtime>,","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/gleam-lang/gleam/blob/7e623aa83da3776faee50ca4ab9a6c40124acd95/compiler-cli/src/run.rs#L21-L57","documentation":"run.rs::command installs a no-op SIGINT handler before exec'ing the compiled program so Ctrl-C is delivered to the child Erlang VM instead of killing gleam. ctrlc::set_handler fails if a handler is already installed for this process (the crate enforces a single handler) or if the signal machinery can't initialize, and the expect(\"Error setting Ctrl-C handler\") turns that into a panic. For a normal one-shot CLI process this is nearly unreachable; it bites code that embeds or re-invokes this command function in the same process.","triggerScenarios":"Calling run::command() twice in one process (integration tests, REPL-style wrappers), or running gleam as a library inside a host that already called ctrlc::set_handler. Exotic case: signal() blocked by a seccomp policy at startup.","commonSituations":"Custom test harnesses that exercise `gleam run` logic in-process; embedding the gleam CLI in a supervisor that manages its own Ctrl-C behavior; orchestrators that install signal handlers before running tasks.","solutions":["Run `gleam run` as a subprocess (spawn the gleam binary) instead of calling the Rust function twice in-process.","If embedding, don't install your own ctrlc handler before calling this code — let gleam own it, or fork.","For maintainers: match on ctrlc::Error::MultipleHandlers and continue instead of expect, since an existing handler still prevents the default exit.","Free the single-handler slot before invoking: drop/never set another handler in the host process."],"exampleFix":"// before: harness sets a handler, then calls gleam's run\nctrlc::set_handler(|| println!(\"interrupt\"))?;\ngleam_cli::run::command(&paths, args, None, None, None, Which::Run, false)?; // panics\n\n// after: exec gleam as a child process, keep your own handler\nlet status = std::process::Command::new(\"gleam\").args([\"run\"]).status()?;","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"// If you must host gleam in-process: probe once and fall back to a subprocess.\nlet ran_inline = std::panic::catch_unwind(|| {\n    // first invocation of run::command(...) — installs the ctrl-c handler\n})\n.is_ok();\nif !ran_inline {\n    // handler slot taken (or other panic): fall back to spawning the gleam binary\n    let _ = std::process::Command::new(\"gleam\").arg(\"run\").status();\n}","preventionTips":["Invoke `gleam run` as a subprocess rather than calling the crate's command function more than once per process.","Only one component per process may own ctrlc::set_handler — never install your own before calling gleam code.","In test harnesses, give each test its own process (or use one cargo test per process) instead of re-entering command()."],"tags":["panic","signal","ctrl-c","sigint","process","gleam-run","erlang"],"backgroundTag":"signal-handler-conflict","analyzedSha":"7e623aa83da3776faee50ca4ab9a6c40124acd95","analyzedAt":"2026-08-17T00:07:02.091Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}