Hmbown/CodeWhale · error
Task admission replay does not match the bound request
Error message
Task admission replay does not match the bound request
What it means
validate_bound_task_request re-checks a replayed admission request against the durable TaskRecord: title/name, cwd, permission_posture, and auto_approve must match exactly (with the same defaults applied). A mismatch means the request would re-admit a different task than the one durably bound, so it is rejected.
Solutions
- Replay the exact original request (same permission_posture, auto_approve, cwd, title) that produced the record
- If intent changed, create a new task instead of replaying admission
- Compare the record's fields first and reconcile the request before validating
Example fix
// before
let req = NewTaskRequest { auto_approve: Some(true), ..orig };
validate(&task, &req)?; // mismatch
// after
let req = NewTaskRequest { auto_approve: Some(task.auto_approve), ..orig };
validate(&task, &req)?; Defensive patterns
Strategy: validation
Validate before calling
if request.auto_approve.unwrap_or(false) != task.auto_approve
|| request.permission_posture.as_deref().unwrap_or_default() != task.permission_posture.as_deref().unwrap_or_default() {
return Err(anyhow!("request would re-admit a different task"));
} Type guard
fn replay_matches(task: &TaskRecord, req: &NewTaskRequest) -> bool {
req.permission_posture.as_deref().map(|p| Some(p) == task.permission_posture.as_deref()).unwrap_or(true)
&& req.auto_approve.unwrap_or(false) == task.auto_approve
} Try / catch
match manager.admit_bound_task(&task, &request).await {
Err(e) if e.to_string().contains("admission replay does not match") => {
// intent changed: create a fresh task instead of replaying
manager.create_task(request.into_fresh()).await?;
}
other => other?,
} Prevention
- Persist the original request alongside the record and replay it verbatim
- Treat replayed admission as immutable: never alter posture/approval fields
- Prefer create-new over replay when any intent changed
When it happens
Trigger: Calling validate_bound_task_request (via admission/recovery) with a NewTaskRequest whose permission_posture differs from the record's, whose auto_approve differs from the record's default, or with any other non-matching bound field.
Common situations: Replaying a saved request after the user changed approval posture or cwd; constructing the request manually with defaults that differ from the stored record; resubmitting an old request against a newer record version.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Bound task identity or schema does not match its durable…
- Invalid pet checkpoint.
- Invalid pet interaction.
- Invalid pet recording.
- Invalid pet recording.
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/2a2a3180d85c89ce.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/task_manager.rs:3134
.as_ref()
.is_some_and(|value| value != &task.workspace)
|| request
.mode
.as_ref()
.is_some_and(|value| value != &task.mode)
|| request
.allow_shell
.is_some_and(|value| value != task.allow_shell)
|| request
.trust_mode
.is_some_and(|value| value != task.trust_mode)
|| request
.permission_posture
.as_deref()
.is_some_and(|value| Some(value) != task.permission_posture.as_deref())
|| task.auto_approve != request.auto_approve.unwrap_or(false)
{
bail!("Task admission replay does not match the bound request");
}
Ok(())
}
/// A read-only inventory and reconstructed queue. Execution recovery is a
/// separate transaction requiring proof that a particular generation died.
struct LoadedTaskState {
tasks: HashMap<String, TaskRecord>,
queue: VecDeque<String>,
}
fn load_state(tasks_dir: &Path, queue_path: &Path) -> Result<LoadedTaskState> {
let mut tasks = HashMap::new();
if tasks_dir.exists() {
for entry in fs::read_dir(tasks_dir)
.with_context(|| format!("Failed to read tasks dir {}", tasks_dir.display()))?
{
let entry = entry?;View on GitHub (pinned to 73e0f67d83)