clockworklabs/SpacetimeDB · error

Environment key : shell input must be UTF-8

Error message

Environment key {:?}: shell input must be UTF-8

What it means

During `resolve` of environment values, the CLI looks up each declared key in the shell environment. If a value is found but cannot be converted to a UTF-8 `String` (it contains invalid bytes, e.g. non-UTF-8 locale data), `into_string()` fails and this error names the offending key. SpacetimedDB environment values must be valid UTF-8 strings.

Solutions

  1. Re-set the offending environment variable with valid UTF-8 content before publishing.
  2. Check encoding: run `locale` and switch to a UTF-8 locale (e.g. `export LANG=C.UTF-8`).
  3. Identify bad bytes with `printf '%s' "$KEY" | xxd` and fix the source of the value.
  4. If the variable shouldn't be picked up from the shell, unset it or supply the value via `--env` instead.

Example fix

// before (shell)
export API_TOKEN=$'caf\xe9'   # Latin-1 bytes
spacetime publish -d mydb     # Environment key "API_TOKEN": shell input must be UTF-8
// after
export API_TOKEN='café'       # UTF-8 encoded
spacetime publish -d mydb
Defensive patterns

Strategy: validation

Validate before calling

// bash: fail early if any declared var is not valid UTF-8
for k in "$@"; do
  printf '%s' "${!k}" | iconv -f UTF-8 -t UTF-8 >/dev/null || { echo "$k is not UTF-8"; exit 1; }
done

Try / catch

// rust: pre-validate env values you inject
fn assert_utf8(key: &str) -> anyhow::Result<()> {
    std::env::var_os(key)
        .map(|v| v.into_string().map_err(|_| anyhow::anyhow!("{key} not UTF-8")))
        .unwrap_or(Ok(()))
}

Prevention

When it happens

Trigger: An environment variable declared by the module contains raw bytes that are not valid UTF-8 (e.g. binary data or a non-UTF-8 encoded value); `std::env::var_os` yields an OsString whose `into_string()` fails.

Common situations: Locales using legacy encodings (e.g. Latin-1) exporting variables with accented characters encoded in non-UTF-8; secrets or tokens pasted from binary sources; variables set by tooling with escaped byte sequences.

Understand the failure class

Background: "is not a valid" / "Invalid ... value" environment variable errors: how libraries validate env vars and what to do when they reject yours — this error's family across 48 libraries.

Related errors


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

Appendix: source

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

        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)
}

#[cfg(test)]
mod tests;

/// Update configuration without inspecting or building a local module.
pub(super) async fn publish_only(
    config: &mut crate::config::Config,
    server: Option<&str>,
    database: &str,
    anonymous: bool,
    yes: super::YesFlags,

View on GitHub (pinned to eddf9f5014)