unicity-aos/aos-ce · warning

aos-mcp: malformed hook ingress

Error message

aos-mcp: malformed {expected_host} hook ingress: {error}

What it means

The MCP capsule's host-hook ingress handler (`handle`) deserializes the incoming payload into HostHookRequest. When serde_json cannot parse the payload, the hook is rejected as `malformed_payload`, a warning is logged naming the host and serde error, and the handler returns Ok(()) — the payload is dropped, not executed.

Solutions

  1. Inspect the logged serde error and fix the payload to match HostHookRequest's fields and types.
  2. Ensure required fields (event, principal_id) are present and correctly typed in the ingress payload.
  3. Verify the sending host bridge version matches the capsule's expected hook schema.

Example fix

// before (host side)
send_hook(host, json!({ "event": "tool_call" }))
// after
send_hook(host, json!({ "event": "tool_call", "principal_id": "abc-123" }))
Defensive patterns

Strategy: try-catch

Validate before calling

// Host side: validate payload shape before sending
if payload.get("principal_id").is_none() || payload.get("event").is_none() {
    panic!("hook ingress payload missing required fields");
}

Type guard

fn is_valid_hook_payload(v: &serde_json::Value) -> bool {
    v.get("event").and_then(|e| e.as_str()).is_some()
        && v.get("principal_id").and_then(|p| p.as_str()).is_some()
}

Try / catch

match serde_json::from_value::<HostHookRequest>(payload) {
    Ok(req) => handle(req),
    Err(e) => log::warn!("malformed hook ingress: {e}"),
}

Prevention

When it happens

Trigger: Host sends a hook ingress payload to handle(expected_host, payload) that is not valid JSON for HostHookRequest (missing required fields like event/principal_id, wrong types, or invalid JSON).

Common situations: Host bridge version drift producing a different payload schema, manual curl testing against the hook with a hand-written body, or serialization bugs in the sending side.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13). Data as JSON: /api/errors/6be5d00f263cabd3. Report an issue: GitHub.

Appendix: source

Thrown at capsules/capsule-mcp/src/host_hooks.rs:69

    host: String,
    session_id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    canonical_hook: Option<String>,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    event: Option<String>,
    correlation_id: String,
    route_id: String,
    delivery_id: String,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    context: Option<String>,
}

pub(crate) fn handle(expected_host: &str, payload: serde_json::Value) -> Result<(), SysError> {
    let request: HostHookRequest = match serde_json::from_value(payload) {
        Ok(request) => request,
        Err(error) => {
            reject(expected_host, "unknown", "malformed_payload");
            log::warn(format!(
                "aos-mcp: malformed {expected_host} hook ingress: {error}"
            ));
            return Ok(());
        }
    };
    if let Err(reason) = validate_request(expected_host, &request) {
        reject(expected_host, &request.event, reason);
        return Ok(());
    }

    let caller = match runtime::caller() {
        Ok(caller) => caller,
        Err(error) => {
            reject(expected_host, &request.event, "caller_unavailable");
            log::warn(format!(
                "aos-mcp: caller unavailable for {expected_host} hook: {error}"
            ));
            return Ok(());

View on GitHub (pinned to f6f22024fb)