BoundaryML/baml · error

Expected a statically defined string, not env variable

Error message

Expected a statically defined string, not env variable

What it means

BAML's UnresolvedValue::as_static_str extracts a literal string from a statically-parsed BAML value (e.g. a model provider option, client name, or metadata field). It refuses to read an env-var reference (env::VAR or $VAR) because doing so requires runtime resolution with an environment context, which this zero-cost static accessor deliberately does not perform. The library throws this to force callers to either use resolve_string(ctx) or provide a literal.

Source

Thrown at engine/baml-lib/baml-types/src/value_expr.rs:519

        }
    }
}

impl Default for EvaluationContext<'_> {
    fn default() -> Self {
        Self {
            env_vars: None,
            fill_missing_env_vars: true,
        }
    }
}

impl<Meta> UnresolvedValue<Meta> {
    pub fn as_static_str(&self) -> Result<&str> {
        match self {
            Self::String(StringOr::Value(v), ..) => Ok(v.as_str()),
            Self::String(StringOr::EnvVar(..), ..) => {
                anyhow::bail!("Expected a statically defined string, not env variable")
            }
            Self::String(StringOr::JinjaExpression(..), ..) => {
                anyhow::bail!("Expected a statically defined string, not expression")
            }
            Self::String(StringOr::TemplateStringCall { .. }, ..) => {
                anyhow::bail!("Expected a statically defined string, not a template_string call")
            }
            Self::Numeric(num, ..) => Ok(num.as_str()),
            Self::Array(..) => anyhow::bail!("Expected a string, not an array"),
            Self::Bool(..) => anyhow::bail!("Expected a string, not a bool"),
            Self::Map(..) => anyhow::bail!("Expected a string, not a map"),
            Self::Null(..) => anyhow::bail!("Expected a string, not null"),
            Self::ClassConstructor(..) => {
                anyhow::bail!("Expected a string, not a class constructor")
            }
        }
    }

View on GitHub (pinned to bd85ce9dee)

Solutions

  1. Replace the env-var reference with a static string literal in the .baml file if the value never varies between environments
  2. Call resolve_string(ctx) with an EvaluationContext holding the env vars instead of as_static_str() in the consuming code
  3. Ensure the env var is set and passed into the BAML runtime (EvaluationContext::new(&env_map, ...)) so resolution happens at runtime, not static parse time

Example fix

// before (baml)
client MyClient {
  provider openai
  options {
    api_key env.OPENAI_API_KEY
  }
}
// after (baml)
client MyClient {
  provider openai
  options {
    api_key "sk-static-or-injected-key"
  }
}
// or in Rust, resolve at runtime instead:
// let s = value.resolve_string(&ctx)?;
Defensive patterns

Strategy: validation

Validate before calling

// Rust
fn needs_static(v: &UnresolvedValue<()>) -> Result<&str> {
    if let UnresolvedValue::String(StringOr::EnvVar(_), _) = v {
        anyhow::bail!("field must be a static string, not env::VAR");
    }
    v.as_static_str()
}

Type guard

fn is_static_string<Meta>(v: &UnresolvedValue<Meta>) -> bool {
    matches!(v, UnresolvedValue::String(StringOr::Value(_), _))
}

Prevention

When it happens

Trigger: Calling as_static_str() on an UnresolvedValue::String(StringOr::EnvVar(..)) — i.e. the BAML source declares the field as an environment-variable reference like BAML_ENV_VAR or "env.MY_KEY" instead of a plain quoted string.

Common situations: Developers parameterize BAML config (model name, API key field, client id) via env vars for dev/prod portability, then a compile-time/static pass (LLM client registration, client schema validation, or tooling that reads baml_src without env vars) tries to read the value statically and fails.

Related errors


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