denoland/deno · error

pledge test permissions called before restoring previous ple

Error message

pledge test permissions called before restoring previous pledge

What it means

Panic in the op_pledge_test_permissions op (cli/ops/bench.rs) backing `deno bench`. The test harness 'pledges' (temporarily replaces) the worker's permission set and stores the original in OpState under PermissionsHolder. Pledging again while a previous pledge is still active finds the stored holder and panics, because only one nested permission swap is supported.

Source

Thrown at cli/ops/bench.rs:64

  state.borrow::<ModuleSpecifier>().to_string()
}

#[derive(Clone)]
struct PermissionsHolder(Uuid, PermissionsContainer);

#[op2(stack_trace)]
#[serde]
pub fn op_pledge_test_permissions(
  state: &mut OpState,
  #[serde] args: ChildPermissionsArg,
) -> Result<Uuid, deno_runtime::deno_permissions::ChildPermissionError> {
  let token = Uuid::new_v4();
  let parent_permissions = state.borrow_mut::<PermissionsContainer>();
  let worker_permissions = parent_permissions.create_child_permissions(args)?;
  let parent_permissions = parent_permissions.clone();

  if state.try_take::<PermissionsHolder>().is_some() {
    panic!("pledge test permissions called before restoring previous pledge");
  }

  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 {

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Restore before pledging again: always pair each pledge with op_restore_test_permissions(token) in a finally block
  2. Review custom harness code that calls these ops and fix the call ordering
  3. If this fires from stock `deno bench` usage, report it as a Deno bug with a minimal reproduction

Example fix

// before (harness pseudo-code)
const t1 = pledge(args);
const t2 = pledge(args);          // panics: previous pledge active
// after
const t1 = pledge(args);
try { runBench(); } finally { restore(t1); }
const t2 = pledge(args);
Defensive patterns

Strategy: validation

Validate before calling

// wrap the internal ops so a second pledge is refused before it panics
const state = { pledged: false };
function safePledge(args) {
  if (state.pledged) throw new Error('previous pledge not restored');
  const token = Deno[Deno.internal].pledgeTestPermissions(args);
  state.pledged = true;
  return token;
}

Prevention

When it happens

Trigger: Calling the internal pledge op twice without an intervening restore — in practice, a custom/forked test runner (or Deno's own harness code misordered) invoking op_pledge_test_permissions before op_restore_test_permissions completed.

Common situations: Contributors modifying cli/js bench/testing harness logic; user code reaching into internal ops via unstable internals; a race where one bench worker re-enters the harness entrypoint. Not reachable through normal Deno.bench() usage.

Related errors


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