BigPizzaV3/CodexPlusPlus · error · anyhow::Error
Remote Control recovery requires a valid thread id
Error message
Remote Control recovery requires a valid thread id
What it means
Thrown by validate_request in remote_control_recovery.rs, which guards every PendingRemoteControlRecovery record before it is persisted to the pending-recovery state file. The thread_id must be a non-blank string of at most 128 bytes after trimming; blank or oversized ids are rejected so the on-disk queue never holds records that can never be matched back to a real codex thread.
Source
Thrown at crates/codex-plus-core/src/remote_control_recovery.rs:150
}
Err(error) => Err(error.into()),
}
}
fn save_state(path: &Path, state: &PendingRemoteControlRecoveryState) -> anyhow::Result<()> {
if state.requests.is_empty() {
match std::fs::remove_file(path) {
Ok(()) => return Ok(()),
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(()),
Err(error) => return Err(error.into()),
}
}
atomic_write(path, serde_json::to_string_pretty(state)?.as_bytes())
}
fn validate_request(request: &PendingRemoteControlRecovery) -> anyhow::Result<()> {
if request.thread_id.trim().is_empty() || request.thread_id.len() > 128 {
anyhow::bail!("Remote Control recovery requires a valid thread id");
}
if request.profile_id.trim().is_empty() || request.target_provider.trim().is_empty() {
anyhow::bail!("Remote Control recovery requires profile and provider provenance");
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
fn request(thread_id: &str) -> PendingRemoteControlRecovery {
PendingRemoteControlRecovery {
thread_id: thread_id.to_string(),
profile_id: "official-mix".to_string(),
target_provider: "custom".to_string(),
config_generation: "generation".to_string(),View on GitHub (pinned to 1f431ae49b)
Solutions
- Pass the real codex thread id (the session file stem / thread_id from the session list), non-empty and <= 128 bytes
- Trim the id at the call site before building the record
- If the id legitimately exceeds 128 bytes, store a hash or mapping instead of the raw value
- Add a UI-side length check so users see the problem before the request reaches the core
Example fix
// before
let req = PendingRemoteControlRecovery {
thread_id: format!("{a}-{b}-{c}", /* ... */),
// ...
};
// after
let thread_id = a.to_string(); // single real thread id
assert!(!thread_id.trim().is_empty() && thread_id.len() <= 128); Defensive patterns
Strategy: validation
Validate before calling
fn valid_thread_id(id: &str) -> bool {
!id.trim().is_empty() && id.len() <= 128
}
if !valid_thread_id(&req.thread_id) {
return Err(anyhow::anyhow!("thread id must be 1..=128 bytes, got {}", req.thread_id.len()));
} Type guard
fn valid_thread_id(id: &str) -> bool {
!id.trim().is_empty() && id.len() <= 128
} Try / catch
match record_pending_remote_control_recovery(path, req) {
Err(e) if e.to_string().contains("valid thread id") => { /* drop the record, log thread_id length */ }
other => other?,
} Prevention
- Derive thread_id only from the codex session list / file stem, never from user free text
- Enforce a 128-byte input maxlength in the frontend field
- Unit-test the boundary: empty, whitespace-only, 128 bytes (ok), 129 bytes (reject)
When it happens
Trigger: Calling record_pending_remote_control_recovery (or complete_pending_remote_control_recovery flows that re-validate) with a PendingRemoteControlRecovery whose thread_id.trim().is_empty() is true, or whose thread_id length exceeds 128 bytes (e.g. an id built by concatenating ids, or a whole session title pasted as the id).
Common situations: Frontend sends an empty string because the thread was never opened; a bridge caller passes null coerced to ""; the id is generated by joining multiple identifiers and grows past 128 bytes; whitespace-only id from a mis-trimmed form field.
Related errors
- Remote Control recovery requires profile and provider proven
- 中转 Key 不能为空
- config.toml 内容不能为空
- {label}必须大于 0
- Remote Control session recovery is unavailable
AI-assisted analysis of BigPizzaV3/CodexPlusPlus@1f431ae49b (2026-08-16).
Data as JSON: /api/errors/bee405bf4b4a89a6.
Report an issue: GitHub.