{"record":{"id":"047b0c90b3c99508","repo":"block/buzz","slug":"gate-off","errorCode":null,"errorMessage":"gate off","messagePattern":"gate off","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/buzz-db/src/runtime/tests.rs","lineNumber":1698,"sourceCode":"    }\n    let cid = CommunityId::from_uuid(community);\n    let writer_only = \"aa\".repeat(32);\n    let replica_only = \"bb\".repeat(32);\n    relay_members::add_relay_member(&writer, cid, &writer_only, \"member\", None)\n        .await\n        .expect(\"seed writer member\");\n    relay_members::add_relay_member(&replica, cid, &replica_only, \"member\", None)\n        .await\n        .expect(\"seed replica member\");\n\n    let mut db = Db::from_pools(writer.clone(), replica.clone());\n    db.fence().force_open_for_tests(chrono::Utc::now());\n\n    // Budget unset ⇒ bounded arm disabled ⇒ writer.\n    assert!(\n        db.is_relay_member(cid, &writer_only)\n            .await\n            .expect(\"gate off\"),\n        \"budget unset must answer from the writer\"\n    );\n    assert!(!db.is_relay_member(cid, &replica_only).await.unwrap());\n\n    // Budget set + fresh entry ⇒ replica.\n    db.set_replica_read_max_age_for_tests(Some(std::time::Duration::from_secs(5)));\n    assert!(\n        db.is_relay_member(cid, &replica_only)\n            .await\n            .expect(\"gate on\"),\n        \"budget set must answer from the replica\"\n    );\n    assert!(!db.is_relay_member(cid, &writer_only).await.unwrap());\n\n    // Entry older than the budget ⇒ fail closed to the writer. Close\n    // first so no prior fresh entry can be the one proved (matches the\n    // count test; today `force_open_for_tests_at` also clears the ring).\n    db.fence().close();","sourceCodeStart":1680,"sourceCodeEnd":1716,"githubUrl":"https://github.com/block/buzz/blob/dad5a33865fc81a2e55b3b60746632f615ec1e3a/crates/buzz-db/src/runtime/tests.rs#L1680-L1716","documentation":"This is `.expect(\"gate off\")` on `db.is_relay_member(cid, &writer_only)` in `is_relay_member_is_bounded_routed_and_fails_closed`. With the replica-read budget unset, the membership check must be answered from the writer; the expect panics if the query returns Err rather than a boolean. Note the expect unwraps a `Result<bool, _>`, so Err propagates as a panic with the message \"gate off\".","triggerScenarios":"Calling `is_relay_member` on the writer-routed arm when the writer pool is unreachable, the `relay_members`/`communities` tables are missing in the scratch DB, or the community id has no row (FK/JOIN error in the membership query).","commonSituations":"Seeding steps above failed silently (but their expects would have panicked first); running the ignored test without Postgres; schema drift in `relay_members` after a migration change; pool closed between fence setup and the query.","solutions":["Ensure Postgres is running and both scratch DBs are created and migrated before the query.","Verify the seed steps (`insert community`, both `add_relay_member` calls) succeeded on the writer.","Print the inner error: `.unwrap_or_else(|e| panic!(\"is_relay_member (gate off) failed: {e}\"))`.","Confirm `Db::from_pools(writer, replica)` got live pools, not ones already dropped by `drop_scratch_db`.","Check `relay_members` membership query still compiles against the current schema."],"exampleFix":"// before\ndb.is_relay_member(cid, &writer_only).await.expect(\"gate off\")\n// after\ndb.is_relay_member(cid, &writer_only).await\n    .unwrap_or_else(|e| panic!(\"is_relay_member (gate off / writer) failed: {e}\"))","handlingStrategy":"try-catch","validationCode":"// Pre-check: writer reachable and member seeded before the routed check\nlet seeded: i64 = sqlx::query_scalar(\"SELECT COUNT(*) FROM relay_members WHERE pubkey=$1\")\n    .bind(&writer_only).fetch_one(&writer).await.unwrap_or(0);\nassert_eq!(seeded, 1, \"writer-only member must be seeded first\");","typeGuard":"fn membership_result_ok(r: &Result<bool, sqlx::Error>) -> bool { r.is_ok() }","tryCatchPattern":"match db.is_relay_member(cid, &writer_only).await {\n    Ok(found) => assert!(found),\n    Err(e) => panic!(\"is_relay_member failed (gate off): {e}\"),\n}","preventionTips":["Run seeding asserts before routed membership checks","Keep both pools open for the whole test lifetime","With the budget unset, expect the writer to answer — an Err indicates infra, not routing","Print inner sqlx errors for diagnosability instead of bare expect labels"],"tags":["postgres","database","test-panic","membership-query"],"backgroundTag":"postgres-query-failed","analyzedSha":"dad5a33865fc81a2e55b3b60746632f615ec1e3a","analyzedAt":"2026-08-30T13:49:18.474Z","contentChangedAt":"2026-08-30T13:49:18.474Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}