clockworklabs/SpacetimeDB · error
Invalid environment read row
Error message
Invalid environment read row
What it means
Thrown by `render` when a row in the environment-read result has a column count different from the expected projection width (2 for `key`/`value` list queries, 1 for `value`-only get queries). It guards row access before reading row[0] and row.last().
Solutions
- Match CLI and server versions to restore row-shape compatibility.
- Inspect the raw JSON response to confirm row arity.
- Remove any intermediary that may be transforming the SQL response.
- Retry to rule out a transient serialization glitch.
Defensive patterns
Strategy: validation
Type guard
fn rows_are_well_formed(r: &SqlStmtResult<Vec<String>>, width: usize) -> bool {
r.rows.iter().all(|row| row.len() == width)
} Prevention
- Keep CLI and server versions compatible.
- Validate row arity when consuming SQL responses programmatically.
- Avoid proxies that reshape responses.
When it happens
Trigger: A parsed row vector's length differs from `expected.len()` — the server returned malformed rows, or the schema/row shapes disagree due to a protocol mismatch.
Common situations: Server/CLI version mismatch; corrupt or hand-modified SQL response from a proxy; a server bug emitting short rows.
Related errors
- Invalid environment read projection
- Invalid environment read response
- Invalid environment read result count
- Cannot extract accessor name from query
- Cannot extract table name from query
AI-assisted analysis of clockworklabs/SpacetimeDB@eddf9f5014 (2026-09-20).
Data as JSON: /api/errors/5318a681bc497d42.
Report an issue: GitHub.
Appendix: source
Thrown at crates/cli/src/subcommands/env.rs:138
// Validate the requested projection before rendering any response values.
let results: Vec<spacetimedb_client_api_messages::http::SqlStmtResult<Vec<String>>> =
serde_json::from_slice(body).map_err(|_| anyhow::anyhow!("Invalid environment read response"))?;
ensure!(results.len() == 1, "Invalid environment read result count");
let result = &results[0];
let expected: &[&str] = match query {
Query::List => &["key", "value"],
Query::Get(_) => &["value"],
};
ensure!(
result.schema.elements.len() == expected.len()
&& result.schema.elements.iter().zip(expected).all(|(column, name)| {
column.name.as_deref() == Some(*name) && column.algebraic_type == spacetimedb_lib::AlgebraicType::String
}),
"Invalid environment read projection"
);
ensure!(result.rows.len() <= MAX_ENV_VARS, "Environment key count exceeds limit");
for row in &result.rows {
ensure!(row.len() == expected.len(), "Invalid environment read row");
if matches!(query, Query::List) {
validate_key(&row[0]).context("Invalid environment key in response")?;
}
ensure!(
row.last().unwrap().len() <= MAX_ENV_VALUE_BYTES,
"Environment read value exceeds limit"
);
}
match query {
Query::List => {
let mut rows: Vec<_> = result.rows.iter().collect();
rows.sort_unstable_by(|a, b| a[0].cmp(&b[0]));
let rows = rows.into_iter().map(|row| {
Ok::<_, std::convert::Infallible>(spacetimedb_lib::sats::product![row[0].as_str(), row[1].as_str()])
});
let table = sql::build_table(
spacetimedb_lib::sats::satn::PsqlClient::SpacetimeDB,
&result.schema,View on GitHub (pinned to eddf9f5014)