warpdotdev/warp · error · anyhow::Error

MCP server '{name}' field 'warp_id' must be non-empty

Error message

MCP server '{name}' field 'warp_id' must be non-empty

What it means

Thrown when an MCP server entry's warp_id is a string that trims to empty and the WellKnownMcpIds flag is enabled (with the flag off, an empty string fails UUID parsing and hits the UUID error instead). It guards the well-known-id bucket from blank values so empty slugs never silently match a server.

Source

Thrown at app/src/ai/agent_sdk/config_file.rs:130

) -> anyhow::Result<Vec<MCPSpec>> {
    let mut uuids: Vec<uuid::Uuid> = Vec::new();
    let mut well_known: Vec<String> = Vec::new();
    let mut json_map: Map<String, Value> = Map::new();

    for (name, config) in mcp_servers {
        let obj = config
            .as_object()
            .ok_or_else(|| anyhow::anyhow!("MCP server '{name}' config must be a JSON object"))?;

        if let Some(warp_id) = obj.get("warp_id").and_then(Value::as_str) {
            if let Ok(uuid) = uuid::Uuid::parse_str(warp_id) {
                uuids.push(uuid);
            } else if !FeatureFlag::WellKnownMcpIds.is_enabled() {
                return Err(anyhow::anyhow!(
                    "MCP server '{name}' field 'warp_id' must be a UUID"
                ));
            } else if warp_id.trim().is_empty() {
                return Err(anyhow::anyhow!(
                    "MCP server '{name}' field 'warp_id' must be non-empty"
                ));
            } else {
                well_known.push(warp_id.to_string());
            }
        } else {
            json_map.insert(name.clone(), config.clone());
        }
    }

    uuids.sort();
    uuids.dedup();
    well_known.sort();
    well_known.dedup();

    let mut specs: Vec<MCPSpec> = uuids.into_iter().map(MCPSpec::Uuid).collect();
    specs.extend(well_known.into_iter().map(MCPSpec::WellKnown));

View on GitHub (pinned to e72fd7aacb)

Solutions

  1. Fill in a real warp_id value (UUID, or a well-known slug on builds that support it).
  2. Remove the warp_id key entirely if the server should be treated as an inline command/url config.
  3. Lint configs for empty-string values before shipping: jq '.mcp_servers | to_entries | map(select(.value.warp_id? == "")) | length == 0'.

Example fix

// before
"mcp_servers": { "db": { "warp_id": "" } }

// after (inline server, no warp_id)
"mcp_servers": { "db": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-postgres"] } }
Defensive patterns

Strategy: validation

Validate before calling

if let Some(warp_id) = obj.get("warp_id").and_then(Value::as_str) {
    anyhow::ensure!(!warp_id.trim().is_empty(), "warp_id must not be blank");
}

Type guard

fn is_non_blank(s: &str) -> bool {
    !s.trim().is_empty()
}

Try / catch

match mcp_specs_from_mcp_servers(&mcp_servers) {
    Err(err) if err.to_string().contains("must be non-empty") => fix_blank_warp_id_and_retry(&mcp_servers),
    rest => rest,
}

Prevention

When it happens

Trigger: Config contains {"server": {"warp_id": ""}} or {"warp_id": " "} while FeatureFlag::WellKnownMcpIds is on: Uuid::parse_str fails, the flag branch is taken, and warp_id.trim().is_empty() routes to this error.

Common situations: Template-generated configs that leave warp_id as an empty placeholder; YAML-to-JSON conversion emitting an empty string for a null field; or an editor auto-completing the key without a value.

Related errors


AI-assisted analysis of warpdotdev/warp@e72fd7aacb (2026-08-16). Data as JSON: /api/errors/9495b5d30fb26956. Report an issue: GitHub.