{"record":{"id":"99aad90f72021b95","repo":"libnyanpasu/clash-nyanpasu","slug":"version-overflow","errorCode":null,"errorMessage":"version overflow","messagePattern":"version overflow","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"backend/nyanpasu-core/src/state/version.rs","lineNumber":38,"sourceCode":"\nimpl AsRef<u64> for Version {\n    fn as_ref(&self) -> &u64 {\n        &self.0\n    }\n}\n\nimpl Version {\n    pub fn new(version: u64) -> Self {\n        Self(version)\n    }\n\n    /// Return the next monotonic version.\n    ///\n    /// Panics if the counter overflows, because wrapping would break CAS\n    /// monotonicity and may make different state changes compare as the same\n    /// version.\n    pub fn next(&self) -> Self {\n        Self(self.0.checked_add(1).expect(\"version overflow\"))\n    }\n}\n\n/// Unique identifier for a state change, used for tracking and acknowledgment purposes.\n#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]\n#[repr(transparent)]\npub struct StateChangeId(pub Version);\n\nimpl Deref for StateChangeId {\n    type Target = u64;\n\n    fn deref(&self) -> &Self::Target {\n        &self.0\n    }\n}\n\nimpl AsRef<u64> for StateChangeId {\n    fn as_ref(&self) -> &u64 {","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/nyanpasu-core/src/state/version.rs#L20-L56","documentation":"StateVersion::next increments a monotonic version counter used for compare-and-swap of state changes. It uses checked_add and panics on overflow because wrapping would break monotonicity and let distinct changes compare as the same version. The panic requires ~u64::MAX increments, so it is a deliberate invariant guard rather than a realistic runtime failure.","triggerScenarios":"Calling next() when the internal counter is at u64::MAX — i.e. after an astronomically large number of state changes within one process lifetime.","commonSituations":"Essentially never in production; could only be forced in a test by constructing a version at u64::MAX and calling next().","solutions":["Nothing to fix in normal use; if hit, restart the process to reset the counter.","In tests, avoid seeding StateVersion with values near u64::MAX unless explicitly testing overflow.","If long-running processes concern you, propose a design change (e.g.128-bit counter) upstream rather than catching the panic.","Treat any occurrence as a bug report: it signals an unbounded state-change loop."],"exampleFix":"// test-only reproduction to avoid\nlet v = StateVersion(u64::MAX);\nlet _ = v.next(); // panics: version overflow\n\n// after: use realistic seeded values\nlet v = StateVersion(0);\nlet _ = v.next();","handlingStrategy":"validation","validationCode":"fn is_safe_to_increment(v: StateVersion) -> bool { v.0 < u64::MAX }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Don't seed StateVersion with u64::MAX in tests.","Restart the process if the counter ever approaches overflow.","Treat occurrence as a signal of an unbounded change loop.","Propose a wider counter upstream for ultra-long-lived processes."],"tags":["panic","overflow","versioning","rust"],"backgroundTag":"value-out-of-range","analyzedSha":"f7dbce2997c633e484f54788035e770b3ee99773","analyzedAt":"2026-09-08T01:24:59.197Z","contentChangedAt":"2026-09-08T01:24:59.197Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}