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/testing.rs) backing `deno test`. Identical logic to the bench variant: pledging swaps the worker's PermissionsContainer for the test's restricted set and stores the original under PermissionsHolder; a second pledge while one is already stored panics, enforcing a single active permission pledge per worker.
Source
Thrown at cli/ops/testing.rs:94
#[derive(Clone)]
pub struct TestIsolateHandle(pub v8::IsolateHandle);
#[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 {
panic!(View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Pair every pledge with its restore (finally) before pledging again in harness code
- Audit custom harness forks for unbalanced pledge/restore paths, including early-exit error paths
- Report a bug with a minimal repro if stock `deno test` hits it
Example fix
// before (harness pseudo-code)
const t1 = pledge(args);
const t2 = pledge(args); // panics
// after
const t1 = pledge(args);
try { runTests(); } finally { restore(t1); }
const t2 = pledge(args); Defensive patterns
Strategy: validation
Validate before calling
// guard the testing harness entrypoint against double pledges
const pledged = { active: false };
function safePledge(args) {
if (pledged.active) throw new Error('restore the previous pledge first');
const t = pledgeOp(args);
pledged.active = true;
return t;
} Prevention
- Model pledge/restore as a bracket: open once, close once, on every control-flow path
- Add assertions in harness forks that OpState has no holder before pledging
When it happens
Trigger: The internal test-harness op is invoked twice without an intervening op_restore_test_permissions — from modified cli/js harness code, custom runners built on internal ops, or a harness re-entry bug.
Common situations: Contributors changing Deno's testing bootstrap; userland code poking internal ops; edge cases like nested test-server startup double-pledging. Not reachable through normal Deno.test() usage.
Related errors
- pledge test permissions called before restoring previous ple
- restore test permissions token does not match the stored tok
- restore test permissions token does not match the stored tok
- BenchContext::end() has already been invoked
- Snapshot does not match:\n\n ${green("[Diff]")} ${green("
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/1c819a76b76f0b24.
Report an issue: GitHub.