{"record":{"id":"386bfba83817270a","repo":"unicity-aos/aos-ce","slug":"dropped-ingress-message-malformed-principal-p-connection","errorCode":null,"errorMessage":"Dropped ingress message: malformed principal {p:?}; connection stays unbound","messagePattern":"Dropped ingress message: malformed principal (.+?); connection stays unbound","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"capsules/capsule-cli/src/lib.rs","lineNumber":567,"sourceCode":"\n    let msg = match serde_json::from_slice::<serde_json::Value>(bytes) {\n        Ok(v) => v,\n        Err(_) => {\n            log::warn(\"Received malformed IPC payload from socket\");\n            return empty;\n        }\n    };\n\n    let message_principal = msg.get(\"principal\").and_then(|p| p.as_str());\n\n    // Resolve the binding decision first — a conflicting or malformed\n    // principal is dropped before any forward, and never mutates the binding.\n    let (forward_as, newly_bound) = match decide_ingress(current_binding, message_principal) {\n        IngressDecision::Bind(p) => (p.clone(), Some(p)),\n        IngressDecision::ForwardAs(p) => (p, None),\n        IngressDecision::Drop { reason } => {\n            match reason {\n                DropReason::InvalidPrincipal(p) => log::warn(format!(\n                    \"Dropped ingress message: malformed principal {p:?}; connection stays unbound\"\n                )),\n                DropReason::PrincipalConflict { bound, claimed } => log::warn(format!(\n                    \"Dropped ingress message: connection bound to {bound:?} but message claimed {claimed:?}\"\n                )),\n            }\n            return empty;\n        }\n    };\n\n    let (Some(topic), Some(payload)) = (\n        msg.get(\"topic\").and_then(|t| t.as_str()),\n        msg.get(\"payload\"),\n    ) else {\n        // No forwardable body, but the principal still binds the connection\n        // (e.g. a bare handshake establishes identity for connect-tracking).\n        // Nothing is forwarded, so the connection's session is never retargeted.\n        log::warn(\"Ingress message has no topic/payload; binding only, nothing forwarded\");","sourceCodeStart":549,"sourceCodeEnd":585,"githubUrl":"https://github.com/unicity-aos/aos-ce/blob/f6f22024fb1e8d122f28a1b4a9f75aee448ae839/capsules/capsule-cli/src/lib.rs#L549-L585","documentation":"handle_ingress asked decide_ingress to classify an inbound message's principal, and the decision came back Drop with DropReason::InvalidPrincipal, meaning the message's principal could not be parsed/validated. The library logs a warning and drops the message; the connection deliberately stays unbound so a malformed identity can never claim or mutate a binding. Nothing is forwarded downstream.","triggerScenarios":"A client sends an ingress message whose principal field is missing, empty, or not a well-formed principal; decide_ingress returns IngressDecision::Drop { reason: DropReason::InvalidPrincipal(p) } and handle_ingress logs this warning and returns an empty outcome.","commonSituations":"Hand-rolled clients emitting a hand-typed principal string; serialization/version drift between client and proxy (old clients sending an id where a principal struct is expected); test scripts posting raw JSON over the IPC socket with a bogus principal field.","solutions":["Inspect the logged {p:?} debug value to see exactly what was parsed as the principal and fix the client to emit a valid principal","Validate the principal field on the client side before sending ingress messages","Check for protocol/version mismatch between the client and capsule-cli and upgrade the client","If the principal is legitimately optional, send the message without a principal field rather than a malformed one"],"exampleFix":"// before\nsend(msg_with_principal(\"user-42\")) // not a valid principal\n// after\nsend(msg_with_principal(Principal::from_str(\"user-42\")?)) // validated principal","handlingStrategy":"validation","validationCode":"fn valid_principal(p: &str) -> bool { !p.trim().is_empty() && p.parse::<Principal>().is_ok() }\nif !valid_principal(&msg.principal) { return Err(\"invalid principal\"); }","typeGuard":"fn as_principal(v: &serde_json::Value) -> Option<Principal> {\n    v.as_str().and_then(|s| Principal::from_str(s).ok())\n}","tryCatchPattern":null,"preventionTips":["Validate principal format client-side before sending ingress messages","Log the rejected principal value when debugging protocol mismatches","Pin client/proxy protocol versions to avoid serialization drift","Never send empty or placeholder principal strings"],"tags":["ingress","principal","validation","ipc"],"backgroundTag":"invalid-identifier-format","analyzedSha":"f6f22024fb1e8d122f28a1b4a9f75aee448ae839","analyzedAt":"2026-09-13T03:04:44.565Z","contentChangedAt":"2026-09-13T03:04:44.565Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}