tinyhumansai/openhuman · warning
channel message not found (404) on {} {}
Error message
channel message not found (404) on {} {} What it means
Bailed by authed_json when a DELETE whose URL contains /channels/ and /messages/ came back 404 and the path was not matched by parse_message_path. This is a deliberate classification (TAURI-R7 defense-in-depth): the 404 is suppressed from Sentry and surfaced as a plain "message not found" error because an unmatched-route delete of an ephemeral message is expected, not a bug. Callers should treat it as "already gone", not as a hard failure.
Source
Thrown at src/api/rest.rs:759
// Defense-in-depth: DELETE 404s on any channel-message path that
// parse_message_path could not parse (e.g. exotic URL variant with extra
// segments). Still an expected backend state — suppress the Sentry event
// without propagating a typed error. Targets OPENHUMAN-TAURI-R7.
// PATCH is handled above and returns the typed
// `ChannelEditUnsupported` for both the parsed and unparsed shapes.
if method == Method::DELETE
&& url.path().contains("/channels/")
&& url.path().contains("/messages/")
{
tracing::debug!(
domain = "backend_api",
operation = "authed_json",
"[backend_api] channel-message 404 on {} {} — path not matched by \
parse_message_path, suppressing Sentry (TAURI-R7 defense-in-depth)",
method.as_str(),
url.path(),
);
anyhow::bail!(
"channel message not found (404) on {} {}",
method.as_str(),
url.path(),
);
}
// 404 on `/announcements/latest` means "no announcement" for
// this best-effort, cosmetic feature — not a code bug. Surface
// a typed `BackendApiError::AnnouncementNotFound` so the caller
// (`announcements::ops::get_latest_announcement`) can degrade to
// `null` instead of propagating an error, without funneling the
// 404 into `report_error`. Targets `TAURI-RUST-HW0` / `TAURI-RUST-KHX`.
if method == Method::GET && is_announcements_latest_path(url.path()) {
tracing::info!(
domain = "backend_api",
operation = "authed_json",
"[backend_api] announcement-not-found 404 on {} {} — surfacing typed error",
method.as_str(),View on GitHub (pinned to a221052e0d)
Solutions
- Treat this error as success for ephemeral-message cleanup — the goal (message absent) is achieved
- If the message should exist, verify the message_id format against the backend's route and parse_message_path expectations
- Do not wrap this specific 404 in retry loops; retrying a not-found delete cannot succeed
Example fix
// before
client.send_channel_delete(ch, msg_id, &jwt).await?; // hard error on 404
// after
if let Err(err) = client.send_channel_delete(ch, msg_id, &jwt).await {
if err.to_string().starts_with("channel message not found (404)") {
tracing::debug!("message already absent: {msg_id}");
} else {
return Err(err);
}
} Defensive patterns
Strategy: fallback
Type guard
fn is_channel_message_not_found(err: &anyhow::Error) -> bool {
err.to_string().starts_with("channel message not found (404)")
} Try / catch
if let Err(err) = client.send_channel_delete(ch, msg_id, &jwt).await {
if is_channel_message_not_found(&err) {
// Expected for ephemeral cleanup: the goal (message absent) is met.
tracing::debug!(domain = "channel", "message already gone: {msg_id}");
} else {
return Err(err);
}
} Prevention
- Treat 404 on ephemeral-message delete as success in cleanup paths
- Never put this delete in an automatic retry loop
- Verify message_id formats against the backend route shape when deletes 404 on messages that should exist
When it happens
Trigger: send_channel_delete targeting a thinking-indicator or ephemeral message the backend already purged, or a message_id whose route shape does not match parse_message_path (e.g. provider-native id format), yielding 404 on DELETE /channels/{ch}/messages/{id}.
Common situations: Cleanup of ephemeral messages racing with backend TTL purge, retrying a delete after the message was already deleted, or a channel whose backend routes were deployed later than the core.
Related errors
- channel is required
- spawned core exited with ${child.exitCode} ${stderrFn()}
- timed out waiting for core at ${coreUrl} ${stderrFn()}
- whatsapp_data write lock poisoned: {e}
- API base URL must be an absolute http(s) URL with host
AI-assisted analysis of tinyhumansai/openhuman@a221052e0d (2026-08-16).
Data as JSON: /api/errors/49072eca0ae2e7b8.
Report an issue: GitHub.