{"record":{"id":"07ffedcd3588b27d","repo":"astrid-runtime/astrid","slug":"second-probe-blocks","errorCode":null,"errorMessage":"second probe blocks","messagePattern":"second probe blocks","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-gateway/src/state.rs","lineNumber":499,"sourceCode":"        Ok(\n            crate::bus_kernel::BusKernelClient::new(bus, caller.principal.clone(), session_id.0)\n                .with_device_key_id(caller.device_key_id.clone()),\n        )\n    }\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn rate_limiter_blocks_within_window() {\n        let mut limiter = RedeemRateLimiter::default();\n        let ip: IpAddr = \"127.0.0.1\".parse().unwrap();\n        let interval = Duration::from_mins(1);\n\n        assert!(limiter.check(ip, interval).is_none());\n        let wait = limiter.check(ip, interval).expect(\"second probe blocks\");\n        assert!(wait > Duration::from_secs(0));\n    }\n\n    #[test]\n    fn rate_limiter_zero_interval_never_blocks() {\n        let mut limiter = RedeemRateLimiter::default();\n        let ip: IpAddr = \"127.0.0.1\".parse().unwrap();\n        let interval = Duration::from_secs(0);\n        // Zero interval: every probe should be free regardless.\n        assert!(limiter.check(ip, interval).is_none());\n        assert!(limiter.check(ip, interval).is_none());\n    }\n\n    #[test]\n    fn signing_material_round_trips() {\n        use ed25519_dalek::{Signer, Verifier};\n        let s = SigningMaterial::fresh();\n        let msg = b\"hello world\";","sourceCodeStart":481,"sourceCodeEnd":517,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-gateway/src/state.rs#L481-L517","documentation":"Test assertion in `RedeemRateLimiter`: after a first successful `check(ip, interval)` returns `None` (allowed), the second call within the window must return `Some(wait)` — the remaining wait duration. The `expect(\"second probe blocks\")` fires when the second probe was unexpectedly allowed (`None`), meaning the rate limiter failed to block a repeat request inside the interval.","triggerScenarios":"Calling `limiter.check(ip, interval)` twice for the same IP within a `Duration::from_mins(1)` window and getting `None` on the second call — the limiter did not record the first probe or reset its window incorrectly.","commonSituations":"A regression in `RedeemRateLimiter::default()` or `check` (e.g. keying by the wrong field, using wall-clock instead of instant comparison, or clearing entries per call); also system clock changes breaking interval math.","solutions":["Verify `check` records the IP's last-probe timestamp on the first call and compares `elapsed < interval` on the second.","Confirm `RedeemRateLimiter::default()` actually initializes its map (not left empty after each check).","Check the interval arithmetic uses a monotonic clock (`Instant`) rather than system time.","Ensure tests don't share a global limiter state that another test reset."],"exampleFix":"// before\nlet wait = limiter.check(ip, interval).expect(\"second probe blocks\");\n// after\nlet wait = limiter.check(ip, interval).unwrap_or_else(|| {\n    panic!(\"rate limiter allowed a second probe within {interval:?}; entries: {:?}\", limiter.entries())\n});","handlingStrategy":"validation","validationCode":"// Rust: assert limiter state before second probe\nassert!(limiter.last_probe(&ip).is_some(), \"first probe must be recorded\");","typeGuard":null,"tryCatchPattern":"// Rust: treat a None on a blocked-expected probe as a test failure with context\nmatch limiter.check(ip, interval) {\n    Some(wait) => assert!(wait > Duration::ZERO),\n    None => panic!(\"second probe within {interval:?} was not blocked\"),\n}","preventionTips":["Use a monotonic clock (Instant) inside rate limiter implementations.","Isolate limiter instances per test; never share global state.","Cover clock-skew and window-reset behavior with dedicated tests.","Keep limiter keying (IP + route) explicit and documented."],"tags":["rust","test-assertion","rate-limiting","regression"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}