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
- If this is an intentional bare handshake, ignore the warning or downgrade it in the client protocol docs
- Ensure the client always sends a string "topic" and a "payload" when it expects forwarding
- Validate message shape client-side before sending (topic: string, payload: present)
- 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
- Include a string topic and payload in every message intended for forwarding
- Distinguish intentional handshake-only messages from accidentally empty ones
- Schema-validate ingress JSON before sending (topic: string required)
- Keep client serialization in sync with the proxy's expected message shape
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
- Dropped ingress message: malformed principal
- Dropped ingress message: connection bound to
- Dropped ingress message to blocked topic
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 noView on GitHub (pinned to f6f22024fb)