BoundaryML/baml · error

could not start the playground server

Error message

could not start the playground server

What it means

Raised via `anyhow::ensure!` in `run_server_inner`: the server was asked to open a playground target, but no playground listener could be started, and the open target requires more than the LSP client channel. Without a listener the playground cannot be served, so startup fails with this message.

Solutions

  1. Check that the playground port is free and the listener bound successfully (look for bind errors earlier in the log)
  2. Set the playground open target to `LspClient` if you only want the URL sent through the LSP client
  3. Enable/fix the playground server configuration (host/port) so a listener can be created
  4. Run `run_playground_server` explicitly if you intend a standalone playground server process

Example fix

// before: requiring browser-open with no listener
let Some(listener) = playground_listener else {
    anyhow::ensure!(open_target == PlaygroundOpenTarget::LspClient,
        "could not start the playground server");

// after: fall back to the LSP client target when no listener is available
let open_target = if playground_listener.is_some() {
    open_target
} else {
    PlaygroundOpenTarget::LspClient
};
let Some(listener) = playground_listener else {
    return run_stdio_loop(&runtime, &writer_tx, &writer_budget, writer_rx, &lsp_sender);
};
Defensive patterns

Strategy: fallback

Validate before calling

// check the playground port is bindable before startup
std::net::TcpListener::bind((host, port)).expect("playground port unavailable");

Try / catch

if playground_listener.is_none() {
    eprintln!("playground server failed to start; falling back to LSP-client open target");
}

Prevention

When it happens

Trigger: Starting the server with a playground open target that requires an HTTP playground server (not `PlaygroundOpenTarget::LspClient`) while `playground_listener` is `None` — i.e. playground server binding/startup failed or was not attempted.

Common situations: Port already in use so the playground listener could not bind; playground server disabled in configuration while an open-playground request targets the browser; misconfigured host/port for the playground.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


AI-assisted analysis of BoundaryML/baml@bd85ce9dee (2026-09-12). Data as JSON: /api/errors/aca12d9da045c0a6. Report an issue: GitHub.

Appendix: source

Thrown at baml_language/crates/baml_lsp_server/src/lib.rs:688

    // The command line's roots are the host's own workspace folders:
    // discovered now, before any client connects (a session that initializes
    // later receives the standing diagnostics in full), and covering their
    // projects for the life of the process. In editor mode they join whatever
    // folders the client announces; in browser mode they are the only ones.
    {
        let roots = workspace_roots.clone();
        runtime
            .owner()
            .post(OwnerEvent::Call(Box::new(move |state| {
                for root in &roots {
                    state.add_host_folder(root);
                }
            })));
    }

    let Some(listener) = playground_listener else {
        anyhow::ensure!(
            open_target == PlaygroundOpenTarget::LspClient,
            "could not start the playground server"
        );
        return run_stdio_loop(&runtime, &writer_tx, &writer_budget, writer_rx, &lsp_sender);
    };

    if open_target == PlaygroundOpenTarget::Browser {
        // Nothing drains `writer_rx` in browser mode (no stdout writer), so
        // the bridge owns it and fans LSP output out to `/api/lsp`.
        let lsp_out_tx_bridge = lsp_out_tx.clone();
        std::thread::Builder::new()
            .name("lsp-ws-bridge".into())
            .spawn(move || {
                while let Ok(frame) = writer_rx.recv() {
                    let _ = lsp_out_tx_bridge.send(frame);
                }
            })?;

View on GitHub (pinned to bd85ce9dee)