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

  1. Match CLI and server versions to restore row-shape compatibility.
  2. Inspect the raw JSON response to confirm row arity.
  3. Remove any intermediary that may be transforming the SQL response.
  4. 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

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


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)