clockworklabs/SpacetimeDB · error

Environment key : config input must be a string, boolean or…

Error message

Environment key {name:?}: config input must be a string, boolean or JSON number

What it means

When publishing, environment values sourced from the spacetimedb config file must be JSON strings, booleans, or numbers. Other JSON types (arrays, objects, null) cannot be coerced to an environment string value, so `resolve` bails with this message naming the offending key.

Solutions

  1. Change the config value for the named key to a string, boolean, or number.
  2. Quote structured values as strings if the literal text is intended (e.g. `MY_KEY: "[1,2]"`).
  3. Remove null placeholders from the environment section.

Example fix

// before (config)
MY_KEY: {nested: true}
// after (config)
MY_KEY: "nested=true"
Defensive patterns

Strategy: validation

Validate before calling

fn valid_env_value(v: &serde_json::Value) -> bool { matches!(v, serde_json::Value::String(_) | serde_json::Value::Bool(_) | serde_json::Value::Number(_)) }

Type guard

fn is_scalar_json(v: &serde_json::Value) -> bool { !matches!(v, serde_json::Value::Object(_) | serde_json::Value::Array(_) | serde_json::Value::Null) }

Try / catch

match result { Err(e) if e.to_string().contains("config input must be") => { /* fix config value type for named key */ }, Err(e) => return Err(e), Ok(v) => v }

Prevention

When it happens

Trigger: A config file's environment section contains a key whose value is an object, array, or null (e.g. `MY_KEY: {a: 1}` or `MY_KEY: null`) during `spacetime publish`.

Common situations: Copy-pasting structured config into the env section; forgetting to quote a value that YAML/JSON5 parses as an object or list; accidentally leaving `null` placeholders.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of clockworklabs/SpacetimeDB@eddf9f5014 (2026-09-20). Data as JSON: /api/errors/adebbe70a301819c. Report an issue: GitHub.

Appendix: source

Thrown at crates/cli/src/subcommands/publish/environment.rs:57

pub(super) fn resolve(
    schema: &EnvironmentSchema,
    config: Option<&Value>,
    mut shell: impl FnMut(&str) -> Option<OsString>,
) -> anyhow::Result<Resolved> {
    let mut resolved = Resolved {
        values: BTreeMap::new(),
        sources: BTreeMap::new(),
    };
    if let Some(config) = config {
        let config = config.as_object().context("Environment config must be an object")?;
        for (name, value) in config {
            spacetimedb_lib::environment::validate_key(name)?;
            let value = match value {
                Value::String(value) => value.clone(),
                Value::Bool(value) => value.to_string(),
                Value::Number(value) => value.to_string(),
                _ => anyhow::bail!("Environment key {name:?}: config input must be a string, boolean or JSON number"),
            };
            resolved.values.insert(name.clone(), value);
            resolved.sources.insert(name.clone(), Source::Config);
        }
    }
    // Lookup only the new artifact's declared names, never enumerate ambient values.
    for declaration in schema.declarations() {
        if let Some(value) = shell(&declaration.name) {
            let value = value
                .into_string()
                .map_err(|_| anyhow::anyhow!("Environment key {:?}: shell input must be UTF-8", declaration.name))?;
            resolved.values.insert(declaration.name.clone(), value);
            resolved.sources.insert(declaration.name.clone(), Source::Shell);
        }
    }
    schema.validate_supplied_values(&resolved.values)?;
    Ok(resolved)
}

View on GitHub (pinned to eddf9f5014)