block/buzz · error

gate off

Error message

gate off

What it means

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".

Source

Thrown at crates/buzz-db/src/runtime/tests.rs:1698

    }
    let cid = CommunityId::from_uuid(community);
    let writer_only = "aa".repeat(32);
    let replica_only = "bb".repeat(32);
    relay_members::add_relay_member(&writer, cid, &writer_only, "member", None)
        .await
        .expect("seed writer member");
    relay_members::add_relay_member(&replica, cid, &replica_only, "member", None)
        .await
        .expect("seed replica member");

    let mut db = Db::from_pools(writer.clone(), replica.clone());
    db.fence().force_open_for_tests(chrono::Utc::now());

    // Budget unset ⇒ bounded arm disabled ⇒ writer.
    assert!(
        db.is_relay_member(cid, &writer_only)
            .await
            .expect("gate off"),
        "budget unset must answer from the writer"
    );
    assert!(!db.is_relay_member(cid, &replica_only).await.unwrap());

    // Budget set + fresh entry ⇒ replica.
    db.set_replica_read_max_age_for_tests(Some(std::time::Duration::from_secs(5)));
    assert!(
        db.is_relay_member(cid, &replica_only)
            .await
            .expect("gate on"),
        "budget set must answer from the replica"
    );
    assert!(!db.is_relay_member(cid, &writer_only).await.unwrap());

    // Entry older than the budget ⇒ fail closed to the writer. Close
    // first so no prior fresh entry can be the one proved (matches the
    // count test; today `force_open_for_tests_at` also clears the ring).
    db.fence().close();

View on GitHub (pinned to dad5a33865)

Solutions

  1. Ensure Postgres is running and both scratch DBs are created and migrated before the query.
  2. Verify the seed steps (`insert community`, both `add_relay_member` calls) succeeded on the writer.
  3. Print the inner error: `.unwrap_or_else(|e| panic!("is_relay_member (gate off) failed: {e}"))`.
  4. Confirm `Db::from_pools(writer, replica)` got live pools, not ones already dropped by `drop_scratch_db`.
  5. Check `relay_members` membership query still compiles against the current schema.

Example fix

// before
db.is_relay_member(cid, &writer_only).await.expect("gate off")
// after
db.is_relay_member(cid, &writer_only).await
    .unwrap_or_else(|e| panic!("is_relay_member (gate off / writer) failed: {e}"))
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-check: writer reachable and member seeded before the routed check
let seeded: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM relay_members WHERE pubkey=$1")
    .bind(&writer_only).fetch_one(&writer).await.unwrap_or(0);
assert_eq!(seeded, 1, "writer-only member must be seeded first");

Type guard

fn membership_result_ok(r: &Result<bool, sqlx::Error>) -> bool { r.is_ok() }

Try / catch

match db.is_relay_member(cid, &writer_only).await {
    Ok(found) => assert!(found),
    Err(e) => panic!("is_relay_member failed (gate off): {e}"),
}

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


AI-assisted analysis of block/buzz@dad5a33865 (2026-08-30). Data as JSON: /api/errors/047b0c90b3c99508. Report an issue: GitHub.