{"record":{"id":"0a9d18391a58c939","repo":"BloopAI/vibe-kanban","slug":"pending-approvals-path-should-be-valid","errorCode":null,"errorMessage":"Pending approvals path should be valid","messagePattern":"Pending approvals path should be valid","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/services/src/services/events/patches.rs","lineNumber":162,"sourceCode":"    fn pending_path(approval_id: &str) -> String {\n        format!(\"{}/{}\", PENDING_PATH, escape_pointer_segment(approval_id))\n    }\n\n    pub fn snapshot(pending: &[crate::services::approvals::ApprovalInfo]) -> Patch {\n        let pending: serde_json::Map<String, serde_json::Value> = pending\n            .iter()\n            .map(|info| {\n                (\n                    info.approval_id.clone(),\n                    serde_json::to_value(info).unwrap_or(serde_json::Value::Null),\n                )\n            })\n            .collect();\n\n        Patch(vec![PatchOperation::Replace(ReplaceOperation {\n            path: PENDING_PATH\n                .try_into()\n                .expect(\"Pending approvals path should be valid\"),\n            value: serde_json::Value::Object(pending),\n        })])\n    }\n\n    pub fn created(info: &crate::services::approvals::ApprovalInfo) -> Patch {\n        let value = serde_json::to_value(info).unwrap_or(serde_json::Value::Null);\n        Patch(vec![PatchOperation::Replace(ReplaceOperation {\n            path: pending_path(&info.approval_id)\n                .try_into()\n                .expect(\"Approval path should be valid\"),\n            value,\n        })])\n    }\n\n    pub fn resolved(approval_id: &str) -> Patch {\n        Patch(vec![PatchOperation::Remove(RemoveOperation {\n            path: pending_path(approval_id)\n                .try_into()","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/BloopAI/vibe-kanban/blob/4deb7eca8f381f7cbc1f9d15515a9ab8f8009053/crates/services/src/services/events/patches.rs#L144-L180","documentation":"This panic comes from an `.expect()` on the conversion of the string constant \"/pending\" into a `json_patch` JSON Pointer type (`try_into()` on `PENDING_PATH`). The `json_patch` crate's path type only parses strings that are valid RFC 6901 JSON Pointers (must start with '/', escape ~ and / correctly). It throws when the string fails that parse, which for a hardcoded constant means the conversion is effectively infallible and the expect is a compile-time-invariant assertion.","triggerScenarios":"Only when `PENDING_PATH` (\"/pending\") somehow fails `json_patch::path::JsonPointer::try_from(&str)` parsing — i.e. if the constant were edited to not start with '/' or to contain invalid escapes. Not triggerable by user input since the path never includes dynamic data.","commonSituations":"A developer edits PENDING_PATH and introduces an invalid JSON Pointer (missing leading '/', empty string, or unescaped '~'/'/' if it were dynamic). Upgrading the json_patch crate to a version with stricter pointer validation could also surface this.","solutions":["Verify PENDING_PATH is a valid JSON Pointer: starts with '/' and any '~' or '/' inside segments are escaped as ~0/~1 (escape_pointer_segment already does this for dynamic parts).","If you changed the constant, restore the leading slash, e.g. \"/pending\".","Replace the expect with a parse at construction time (e.g. JsonPointer::from_static or a once-built static) so an invalid constant fails at compile/startup rather than per event.","Pin or update json_patch to a compatible version if the parse rules changed after an upgrade."],"exampleFix":"// before\nconst PENDING_PATH: &str = \"pending\";\npath: PENDING_PATH.try_into().expect(\"Pending approvals path should be valid\"),\n// after\nconst PENDING_PATH: &str = \"/pending\"; // valid JSON Pointer\npath: PENDING_PATH.try_into().expect(\"Pending approvals path should be valid\"),","handlingStrategy":"validation","validationCode":"// Compile-time/startup guard for the constant\nfn assert_valid_pointer(p: &str) {\n    assert!(p.starts_with('/'), \"invalid JSON Pointer: {p}\");\n    json_patch::Pointer::try_from(p).expect(\"PENDING_PATH must be a valid JSON Pointer\");\n}\nconst PENDING_PATH: &str = \"/pending\";\nconst _: () = assert!(\"/pending\".starts_with('/'));","typeGuard":"fn is_valid_pointer(s: &str) -> bool {\n    s.starts_with('/') && json_patch::Pointer::try_from(s).is_ok()\n}","tryCatchPattern":"// Rust cannot catch panics safely in normal flow; prefer non-panicking parse:\nmatch PENDING_PATH.try_into() {\n    Ok(path) => /* build patch */,\n    Err(e) => log::error!(\"invalid pending path: {e}\"),\n}","preventionTips":["Keep path constants as literal valid JSON Pointers and add a unit test parsing each constant.","Always run dynamic segments through escape_pointer_segment.","Build Pointer values once (lazy static) instead of per call.","Add CI tests that construct every patch helper to catch path regressions."],"tags":["rust","json-patch","json-pointer","panic","internal-invariant"],"backgroundTag":"invalid-json-pointer","analyzedSha":"4deb7eca8f381f7cbc1f9d15515a9ab8f8009053","analyzedAt":"2026-08-29T09:24:13.446Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}