{"record":{"id":"0e4795c59d2ba52f","repo":"stalwartlabs/stalwart","slug":"invalid-jump-position","errorCode":null,"errorMessage":"Invalid jump position","messagePattern":"Invalid jump position","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/common/src/expr/parser.rs","lineNumber":259,"sourceCode":"                _ => {}\n            }\n        }\n    }\n\n    fn dec_arg_count(&mut self) {\n        if let Some(x) = self.arg_count.last_mut() {\n            *x = x.saturating_sub(1);\n        }\n    }\n\n    fn update_jmp_pos(&mut self, jmp_pos: Option<usize>) {\n        if let Some(jmp_pos) = jmp_pos {\n            let cur_pos = self.output.len();\n            if let ExpressionItem::JmpIf { pos, .. } = &mut self.output[jmp_pos] {\n                *pos = (cur_pos - jmp_pos) as u32;\n            } else {\n                #[cfg(test)]\n                panic!(\"Invalid jump position\");\n            }\n        }\n    }\n}\n\nimpl BinaryOperator {\n    fn precedence(&self) -> i32 {\n        match self {\n            BinaryOperator::Multiply | BinaryOperator::Divide => 7,\n            BinaryOperator::Add | BinaryOperator::Subtract => 6,\n            BinaryOperator::Gt | BinaryOperator::Ge | BinaryOperator::Lt | BinaryOperator::Le => 5,\n            BinaryOperator::Eq | BinaryOperator::Ne => 4,\n            BinaryOperator::Xor => 3,\n            BinaryOperator::And => 2,\n            BinaryOperator::Or => 1,\n        }\n    }\n}","sourceCodeStart":241,"sourceCodeEnd":277,"githubUrl":"https://github.com/stalwartlabs/stalwart/blob/e96200385781a6a9995a8b839ac27d6c75a983ee/crates/common/src/expr/parser.rs#L241-L277","documentation":"In the expression parser, update_jmp_pos patches the jump offset of a previously emitted JmpIf instruction once its target position is known. If the item at the recorded position is not a JmpIf, the recorded jump position is corrupted — a parser invariant violation. The panic is compiled in only under `cfg(test)`, so in release builds the mismatch is silently ignored and the failure surfaces as a test-time \"Invalid jump position\" panic.","triggerScenarios":"Parsing an expression whose control-flow bookkeeping gets out of sync — e.g. a jump was recorded (jmp_pos pushed) but the output slot was later overwritten with a non-JmpIf item, or positions were recorded/emitted in the wrong order while parsing conditional operators. Running parser tests with such input triggers the panic inside update_jmp_pos, called from parse.","commonSituations":"Developers modifying the parser's output emission or jump bookkeeping (adding new operators or short-circuit handling) and running the test suite; malformed expressions exercising edge cases in jump patching during test runs.","solutions":["Audit the parse path that pushes jmp_pos: ensure a JmpIf is emitted at exactly the recorded index before update_jmp_pos runs.","Check that no code path replaces or reorders self.output entries between emitting JmpIf and patching it.","Run the parser tests with debug output of self.output to find where the recorded slot no longer holds JmpIf."],"exampleFix":"// before: emitting a non-jump item into a slot that had a pending JmpIf\nself.output[jmp_pos] = ExpressionItem::Const(...);\n// after: push new items; never overwrite pending JmpIf slots\nself.output.push(ExpressionItem::Const(...));\n// then update_jmp_pos(Some(jmp_pos)) patches the untouched JmpIf","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"fn is_jmp_if(item: &ExpressionItem) -> bool {\n    matches!(item, ExpressionItem::JmpIf { .. })\n}","tryCatchPattern":"// test-only panic; in library code prefer a no-op or assert with context:\nif let Some(jmp_pos) = jmp_pos {\n    debug_assert!(matches!(self.output[jmp_pos], ExpressionItem::JmpIf { .. }),\n        \"jmp slot {jmp_pos} does not hold JmpIf\");\n}","preventionTips":["Never overwrite or reorder self.output slots once a jump position has been recorded.","Keep jmp recording and JmpIf emission adjacent in the parse code.","Add debug_assert! invariants so mismatches surface immediately in dev builds."],"tags":["parser","internal-invariant","expressions","rust"],"backgroundTag":"internal-invariant-violation","analyzedSha":"e96200385781a6a9995a8b839ac27d6c75a983ee","analyzedAt":"2026-09-06T22:07:17.982Z","contentChangedAt":"2026-09-06T22:07:17.982Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}