{"record":{"id":"704e740edfb157eb","repo":"clockworklabs/SpacetimeDB","slug":"estimated-cardinality-estimate-rows-exceeds-li","errorCode":null,"errorMessage":"Estimated cardinality ({estimate} rows) exceeds limit ({limit} rows)","messagePattern":"Estimated cardinality \\((.+?) rows\\) exceeds limit \\((.+?) rows\\)","errorType":"validation","errorClass":"DBError::Other","httpStatus":null,"severity":"error","filePath":"crates/core/src/estimation.rs","lineNumber":27,"sourceCode":"\n/// If the caller is not allowed to exceed the row limit,\n/// reject the request if the estimated cardinality exceeds the limit.\npub fn check_row_limit<Query>(\n    queries: &[Query],\n    db: &RelationalDB,\n    tx: &Tx,\n    row_est: impl Fn(&Query, &Tx) -> u64,\n    auth: &AuthCtx,\n) -> Result<(), DBError> {\n    if !auth.exceed_row_limit()\n        && let Some(limit) = db.row_limit(tx)?\n    {\n        let mut estimate: u64 = 0;\n        for query in queries {\n            estimate = estimate.saturating_add(row_est(query, tx));\n        }\n        if estimate > limit {\n            return Err(DBError::Other(anyhow::anyhow!(\n                \"Estimated cardinality ({estimate} rows) exceeds limit ({limit} rows)\"\n            )));\n        }\n    }\n    Ok(())\n}\n\n/// Use cardinality estimates to predict the total number of rows scanned by a query.\npub fn estimate_rows_scanned(tx: &Tx, plan: &PhysicalPlan) -> u64 {\n    match plan {\n        PhysicalPlan::TableScan(..) | PhysicalPlan::IxScan(..) => row_estimate(tx, plan),\n        PhysicalPlan::Filter(input, _) => estimate_rows_scanned(tx, input).saturating_add(row_estimate(tx, input)),\n        PhysicalPlan::NLJoin(lhs, rhs) => estimate_rows_scanned(tx, lhs)\n            .saturating_add(estimate_rows_scanned(tx, rhs))\n            .saturating_add(row_estimate(tx, lhs).saturating_mul(row_estimate(tx, rhs))),\n        PhysicalPlan::IxJoin(IxJoin { lhs, unique: true, .. }, _) => {\n            estimate_rows_scanned(tx, lhs).saturating_add(row_estimate(tx, lhs))\n        }","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/core/src/estimation.rs#L9-L45","documentation":"Before executing SQL, handling a call, or registering a subscription, the node estimates how many rows the statements will affect or scan (summing per-query estimates) and compares the total against the database's configured row_limit; the check is skipped only for auth contexts that may exceed row limits. If the estimate exceeds the limit, the request is rejected up front to protect the node from oversized workloads.","triggerScenarios":"A multi-row INSERT or INSERT ... SELECT whose estimated cardinality exceeds row_limit; a subscription whose query plane matches more rows than allowed; a reducer call expected to write more rows in one transaction than the limit permits.","commonSituations":"Seeding or importing large tables in one statement; broad subscriptions like SELECT * FROM big_table; default row_limit too small for the workload; bulk jobs migrated from non-limited environments.","solutions":["Split the work into smaller batches (chunked inserts, repeated calls).","Narrow subscriptions with WHERE clauses so estimated matched rows stay under the limit.","Raise the database's row limit in the server configuration if your deployment allows it.","On builds whose auth supports it, run under an auth context permitted to exceed row limits."],"exampleFix":"-- before: single giant statement\nINSERT INTO ticks SELECT * FROM staging; -- estimate 10,000,000 rows\n-- after: batched under the limit\nINSERT INTO ticks SELECT * FROM staging LIMIT 100000; -- repeat per batch\n\n// subscription (before): SELECT * FROM ticks;\n// subscription (after):  SELECT * FROM ticks WHERE player_id = :me;","handlingStrategy":"validation","validationCode":"async function safeSubscribe(db: DbConnection, query: string, limit: number): Promise<void> {\n  // sanity-check expected size before subscribing\n  const [{ n }] = await db.sql`SELECT COUNT(*) AS n FROM (${query.raw})`.then(r => r.rows as any);\n  if (Number(n) > limit) throw new Error(`query would match ${n} rows > limit ${limit} — add a WHERE filter`);\n  await db.subscriptionBuilder().subscribe([query]);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await db.sql`INSERT INTO ticks SELECT * FROM staging`;\n} catch (e) {\n  if (String(e).includes('exceeds limit')) {\n    await insertInChunks('staging', 'ticks', chunkRows: 100_000); // retry with smaller batches\n    return;\n  }\n  throw e;\n}","preventionTips":["Chunk bulk inserts below the configured row_limit.","Scope subscriptions with WHERE clauses instead of SELECT *.","Know your deployment's row_limit before importing data."],"tags":["spacetimedb","row-limit","cardinality","sql","subscription","capacity"],"backgroundTag":"row-limit-exceeded","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}