unicity-aos/aos-ce · info

Ingress message has no topic/payload; binding only, nothing…

Error message

Ingress message has no topic/payload; binding only, nothing forwarded

What it means

An ingress message carried a principal (so it could bind the connection) but had no topic/payload body to forward. The library logs a warning, keeps the binding (newly_bound is preserved), and returns IngressOutcome { newly_bound, session_id: None } — nothing is forwarded and no session is retargeted. This supports bare handshake messages that exist only to establish identity for connect-tracking.

Solutions

  1. If this is an intentional bare handshake, ignore the warning or downgrade it in the client protocol docs
  2. Ensure the client always sends a string "topic" and a "payload" when it expects forwarding
  3. Validate message shape client-side before sending (topic: string, payload: present)
  4. Check for serialization bugs where the topic/payload fields are omitted or mistyped

Example fix

// before
{"principal": p} // binding only, nothing forwarded
// after
{"principal": p, "topic": "events", "payload": {...}}
Defensive patterns

Strategy: validation

Validate before calling

fn forwardable(msg: &serde_json::Value) -> bool {
    msg.get("topic").and_then(|t| t.as_str()).is_some()
        && msg.get("payload").map_or(false, |p| !p.is_null())
}

Type guard

fn extract_topic(msg: &serde_json::Value) -> Option<&str> {
    msg.get("topic").and_then(|t| t.as_str())
}

Prevention

When it happens

Trigger: A client sends a message with a valid principal but the let-else `msg.get("topic")...else` fails because the "topic" is not a string or "payload" is missing; handle_ingress logs this warning and returns without forwarding.

Common situations: Handshake-only clients that connect just to register identity; clients with a serialization bug dropping the topic field; JSON where topic is a non-string value (number/null) so t.as_str() returns None.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at capsules/capsule-cli/src/lib.rs:585

                DropReason::InvalidPrincipal(p) => log::warn(format!(
                    "Dropped ingress message: malformed principal {p:?}; connection stays unbound"
                )),
                DropReason::PrincipalConflict { bound, claimed } => log::warn(format!(
                    "Dropped ingress message: connection bound to {bound:?} but message claimed {claimed:?}"
                )),
            }
            return empty;
        }
    };

    let (Some(topic), Some(payload)) = (
        msg.get("topic").and_then(|t| t.as_str()),
        msg.get("payload"),
    ) else {
        // No forwardable body, but the principal still binds the connection
        // (e.g. a bare handshake establishes identity for connect-tracking).
        // Nothing is forwarded, so the connection's session is never retargeted.
        log::warn("Ingress message has no topic/payload; binding only, nothing forwarded");
        return IngressOutcome {
            newly_bound,
            session_id: None,
        };
    };

    if !is_allowed_ingress_topic(topic) {
        // A blocked-topic message is neither forwarded nor allowed to retarget
        // the connection's session — otherwise a client could spoof itself onto
        // another session's stream with an unforwarded message.
        log::warn(format!("Dropped ingress message to blocked topic: {topic}"));
        return IngressOutcome {
            newly_bound,
            session_id: None,
        };
    }

    // Always forward under the connection's bound principal. There is no

View on GitHub (pinned to f6f22024fb)