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
- Re-set the offending environment variable with valid UTF-8 content before publishing.
- Check encoding: run `locale` and switch to a UTF-8 locale (e.g. `export LANG=C.UTF-8`).
- Identify bad bytes with `printf '%s' "$KEY" | xxd` and fix the source of the value.
- 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
- Use UTF-8 locales (LANG/LC_ALL=C.UTF-8) in CI and shells that set module env vars.
- Never store binary data in environment variables consumed by modules.
- Re-encode values coming from legacy systems before exporting them.
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
- Cannot read environment schema: HTTP
- Environment key count exceeds limit
- Environment key is absent
- Environment key is both supplied and removed
- Environment read failed with HTTP
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)