denoland/deno · error

restore test permissions token does not match the stored tok

Error message

restore test permissions token does not match the stored token

What it means

Panic in the op_restore_test_permissions op (cli/ops/bench.rs) backing `deno bench`. Each pledge returns a random UUID token stored with the saved permissions; restore must present the matching token. If the caller passes a token different from the one currently stored (stale token from an earlier pledge, regenerated token, or cross-worker mix-up), the op panics rather than restoring the wrong permissions.

Source

Thrown at cli/ops/bench.rs:83

  }

  state.put::<PermissionsHolder>(PermissionsHolder(token, parent_permissions));

  // NOTE: This call overrides current permission set for the worker
  state.put::<PermissionsContainer>(worker_permissions);

  Ok(token)
}

#[op2]
pub fn op_restore_test_permissions(
  state: &mut OpState,
  #[serde] token: Uuid,
) -> Result<(), JsErrorBox> {
  match state.try_take::<PermissionsHolder>() {
    Some(permissions_holder) => {
      if token != permissions_holder.0 {
        panic!(
          "restore test permissions token does not match the stored token"
        );
      }

      let permissions = permissions_holder.1;
      state.put::<PermissionsContainer>(permissions);
      Ok(())
    }
    _ => Err(JsErrorBox::generic("no permissions to restore")),
  }
}

static NEXT_ID: AtomicUsize = AtomicUsize::new(0);

#[allow(clippy::too_many_arguments, reason = "op")]
#[op2]
fn op_register_bench(
  state: &mut OpState,

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Propagate the exact token returned by the pledge call to the matching restore call (keep it in a local variable, single owner)
  2. Guarantee one restore per pledge, in a finally block, with no interleaving pledges
  3. If stock `deno bench` triggers this, file a Deno issue with the reproduction

Example fix

// before
const t = pledge(args);
restore('00000000-0000-0000-0000-000000000000'); // wrong token
// after
const t = pledge(args);
try { runBench(); } finally { restore(t); }
Defensive patterns

Strategy: validation

Validate before calling

// keep exactly one live token and reuse it for the matching restore
let liveToken = null;
function pledge(args) { liveToken = pledgeOp(args); return liveToken; }
function restore(token) {
  if (token !== liveToken) throw new Error('stale pledge token; refusing to call restore');
  restoreOp(token); liveToken = null;
}

Prevention

When it happens

Trigger: Calling op_restore_test_permissions with a UUID that is not the token returned by the most recent op_pledge_test_permissions call in the same worker — e.g. reusing a token after a second pledge replaced the stored holder.

Common situations: Custom bench-harness forks caching tokens across pledge cycles; concurrent paths in harness code both trying to restore; copy-paste of harness snippets with hardcoded/old tokens. Not reachable through normal Deno.bench() usage.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/8935e272b40ab001. Report an issue: GitHub.