unicity-aos/aos-ce · warning
meta-harness: malformed canonical payload
Error message
meta-harness: malformed canonical payload: {error} What it means
After the HookRequest parses and a correlation_id routes it, meta-harness parses request.payload (a JSON string) into CanonicalHostPayload. If the embedded canonical payload string does not deserialize, this warning is logged and the hook returns Ok(()), skipping reflection for the turn.
Solutions
- Print request.payload and validate it against CanonicalHostPayload's expected fields; fix the emitting side.
- Add or relax serde attributes (deny_unknown_fields removal, Option fields) to match the current canonical schema.
- Ensure the sender serializes the canonical payload as a JSON string, not an object, if the struct expects from_str.
- Pin/upgrade capsule-meta-harness and payload producer to matching versions.
Example fix
// before
let canonical: CanonicalHostPayload = serde_json::from_str(&request.payload)?;
// after
// ensure payload contains required fields, e.g. {"source_event":"user_prompt_submit", ...}
let canonical: CanonicalHostPayload = serde_json::from_str(&request.payload)?; Defensive patterns
Strategy: validation
Validate before calling
// validate inner canonical payload before dispatch
const inner = JSON.parse(request.payload);
if (inner.source_event !== 'user_prompt_submit') throw new Error('unsupported event'); Type guard
fn is_canonical(s: &str) -> bool {
serde_json::from_str::<CanonicalHostPayload>(s).is_ok()
} Try / catch
let canonical: CanonicalHostPayload = match serde_json::from_str(&request.payload) {
Ok(c) => c,
Err(e) => { log::warn("malformed canonical payload: {e}"); return Ok(()); }
}; Prevention
- Serialize canonical payload as a JSON string, not a nested object
- Version the canonical payload schema
- Round-trip test producer output against the consumer struct
When it happens
Trigger: request.payload is a JSON string that fails serde_json::from_str::<CanonicalHostPayload> — inner JSON malformed, missing required fields like source_event, or wrong field types inside the nested payload.
Common situations: Producer of the canonical payload changed its schema (e.g., renamed source_event); double-encoding issues where payload is an object rather than a string; truncated payloads from upstream.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- meta-harness: malformed hook request
- Failed to parse from prompt builder
- failed to deserialize IPC message payload
- failed to deserialize compaction response payload
- canonical document exceeds bound
AI-assisted analysis of unicity-aos/aos-ce@f6f22024fb (2026-09-13).
Data as JSON: /api/errors/665c5c36cb5ee385.
Report an issue: GitHub.
Appendix: source
Thrown at capsules/capsule-meta-harness/src/lib.rs:79
pub fn on_message_received(&self, payload: serde_json::Value) -> Result<(), SysError> {
let request: HookRequest = match serde_json::from_value(payload) {
Ok(request) => request,
Err(error) => {
log::warn(format!("meta-harness: malformed hook request: {error}"));
return Ok(());
}
};
let Some(correlation_id) = request.correlation_id else {
return Ok(());
};
if !is_correlation(&correlation_id) {
log::warn("meta-harness: ignored unroutable hook correlation");
return Ok(());
}
let canonical: CanonicalHostPayload = match serde_json::from_str(&request.payload) {
Ok(canonical) => canonical,
Err(error) => {
log::warn(format!(
"meta-harness: malformed canonical payload: {error}"
));
return Ok(());
}
};
if canonical.source_event != "user_prompt_submit"
|| !matches!(canonical.host.as_str(), "codex" | "claude" | "grok")
|| canonical.session_id.is_empty()
{
return Ok(());
}
let Some(context) = activation_context()? else {
return Ok(());
};
ipc::publish_json(
&format!("hook.v1.response.message_received.{correlation_id}"),
&serde_json::json!({ "additional_context": context }),
)View on GitHub (pinned to f6f22024fb)