abhigyanpatwari/GitNexus · error · ValueError

task binding definition drifted for {task['id']}

Error message

task binding definition drifted for {task['id']}

What it means

Raised by resolve_task_bindings when an `expected` pin's definition fields (id, class, repo_identity, ref, prompt_digest, setup_digest, verify_digest, expensive, sandbox_copy, sandbox_dependencies, plus oracle/dependency digests) do not match the freshly-computed definition for the current task (runner_tasks.py:163-166). This is the drift detector: the pinned binding no longer reflects the current task text/config.

Source

Thrown at eval/workflow_bench/runner_tasks.py:166

                repo=repo_identity,
                resolved_sha=resolved_sha.lower(),
            )
        else:
            dependency_binding = task_asset_cache.prepare(
                task,
                repo=repo_identity,
                resolved_sha=resolved_sha.lower(),
            ).dependency_binding
        definition = _task_definition_binding(
            task,
            repo_identity,
            oracle_snapshot,
            dependency_binding,
        )
        if expected is not None:
            supplied_definition = {key: supplied.get(key) for key in definition}
            if supplied_definition != definition:
                raise ValueError(f"task binding definition drifted for {task['id']}")
        bindings.append({**definition, "resolved_sha": resolved_sha.lower()})
    return bindings


def selected_task_bindings(tasks: list[dict[str, Any]]) -> list[dict[str, Any]]:
    """Compatibility wrapper for callers that need newly resolved task pins."""

    return resolve_task_bindings(tasks)

View on GitHub (pinned to d540b00184)

Solutions

  1. Regenerate the bindings JSON from the current tasks/repo state (omit --task-bindings-json once).
  2. If the change is intentional, commit the new bindings alongside the task edit.
  3. Compare each definition key in the pin against the freshly resolved one to find which field drifted.
Defensive patterns

Strategy: validation

Validate before calling

# Detect drift by re-resolving without the pin and comparing definition keys.
fresh = resolve_task_bindings(tasks, None)
for old, new in zip(expected, fresh):
    drift = {k: (old.get(k), new[k]) for k in new if old.get(k) != new[k]}
    if drift:
        raise SystemExit(f"binding drift for {new['id']}: {drift}")

Try / catch

try:
    bindings = resolve_task_bindings(tasks, expected)
except ValueError as exc:
    if "definition drifted" in str(exc):
        # task text/config changed intentionally — refresh the pin
        expected = resolve_task_bindings(tasks, None)
        bindings = resolve_task_bindings(tasks, expected)
    else:
        raise

Prevention

When it happens

Trigger: The task prompt, verify, setup, repo path, sandbox_copy, sandbox_dependencies, or oracle declaration changed since the pin was recorded; the repo_identity resolved differently (different checkout location); ref changed.

Common situations: Editing a prompt and reusing a stale pin; moving the repo checkout; changing sandbox_dependencies without regenerating bindings; the pin was captured against a different ref.

Related errors


AI-assisted analysis of abhigyanpatwari/GitNexus@d540b00184 (2026-08-12). Data as JSON: /api/errors/f89fbe2a057aedd5. Report an issue: GitHub.