BoundaryML/baml · error · PlaygroundNotConfigured

Playground server requires either BAML_PLAYGROUND_DEV_PORT o

Error message

Playground server requires either BAML_PLAYGROUND_DEV_PORT or BAML_PLAYGROUND_DIR

What it means

PlaygroundNotConfigured is a thiserror error thrown when the LSP playground server is started without any way to serve the frontend. The server needs either a Vite dev-server port (BAML_PLAYGROUND_DEV_PORT) to proxy to, or a directory of prebuilt static assets (BAML_PLAYGROUND_DIR). If neither env var is set, there is no UI to serve and startup is aborted.

Source

Thrown at baml_language/crates/baml_lsp_server/src/playground_server.rs:71

    playground_runs::{
        overlay_function_name_for_target, patch_to_wire, run_summary_to_wire, run_to_wire,
    },
    playground_seam::{PlaygroundSeam, PlaygroundSourceFile},
    playground_ws::{RunListFilter, RunListKind, RunListVisibility, WsInMessage, WsOutMessage},
};

/// Telemetry reads one session may run at once. Small on purpose: these are
/// `DataFusion` queries and CAS reads, and a panel needs a couple in flight
/// (the list plus the open execution), not a backlog.
const MAX_INFLIGHT_TELEMETRY: usize = 4;

/// Reported when that ceiling is reached. The client treats it as "skip this
/// refresh" rather than as a failure, because the common cause is its own
/// polling outrunning a slow query.
const TELEMETRY_BUSY_CODE: &str = "telemetryBusy";

#[derive(Debug, thiserror::Error)]
#[error("Playground server requires either BAML_PLAYGROUND_DEV_PORT or BAML_PLAYGROUND_DIR")]
pub struct PlaygroundNotConfigured;

fn to_ws_text(msg: &WsOutMessage) -> Option<AxumWsMsg> {
    match serde_json::to_string(msg) {
        Ok(json) => Some(AxumWsMsg::Text(json.into())),
        Err(e) => {
            tracing::error!("Playground WS: failed to serialize message: {e}");
            None
        }
    }
}

fn epoch_ms() -> u64 {
    let millis = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .unwrap_or_default()
        .as_millis();
    u64::try_from(millis).unwrap_or(u64::MAX)

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Set BAML_PLAYGROUND_DIR to the directory containing the built playground static assets (e.g. baml_studio build output).
  2. Or set BAML_PLAYGROUND_DEV_PORT to the port of a running Vite dev server (e.g. 3000) to proxy UI requests.
  3. Use the packaged/production binary that ships with bundled playground assets instead of a from-source run.
  4. Export the env var in your shell profile or launcher so child LSP processes inherit it.

Example fix

// before
// no env vars set -> PlaygroundNotConfigured
// spawn_lsp()

// after
// BAML_PLAYGROUND_DIR=./baml_studio/dist spawn_lsp()
Defensive patterns

Strategy: validation

Validate before calling

if std::env::var("BAML_PLAYGROUND_DEV_PORT").is_err()
    && std::env::var("BAML_PLAYGROUND_DIR").is_err()
{
    eprintln!("playground disabled: set BAML_PLAYGROUND_DEV_PORT or BAML_PLAYGROUND_DIR");
}

Prevention

When it happens

Trigger: Starting the playground server via run/build_router with neither BAML_PLAYGROUND_DEV_PORT nor BAML_PLAYGROUND_DIR present in the environment; e.g. running the LSP server in production without the packaged playground assets and without a dev server.

Common situations: Developers running the LSP server from source without a Vite dev server; CI/headless environments where env vars were not exported; a wrapper process stripping environment variables; expecting the binary to bundle assets but BAML_PLAYGROUND_DIR unset.

Understand the failure class

Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.

Related errors


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