{"record":{"id":"ce17265e9a6e442f","repo":"napi-rs/napi-rs","slug":"invalidarg","errorCode":"InvalidArg","errorMessage":"CurrentThread host registration was not reserved or was already consumed","messagePattern":"CurrentThread host registration was not reserved or was already consumed","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/async-runtime/src/adapter.rs","lineNumber":342,"sourceCode":"      )\n    })?;\n  RESERVED_HOST_REGISTRATIONS\n    .lock()\n    .unwrap_or_else(std::sync::PoisonError::into_inner)\n    .insert(id);\n  Ok(id)\n}\n\nfn claim_host_registration_id(registration_high: u32, registration_low: u32) -> napi::Result<u64> {\n  let id = BindingHostRegistration::id(registration_high, registration_low);\n  if RESERVED_HOST_REGISTRATIONS\n    .lock()\n    .unwrap_or_else(std::sync::PoisonError::into_inner)\n    .remove(&id)\n  {\n    Ok(id)\n  } else {\n    Err(napi::Error::new(\n      napi::Status::InvalidArg,\n      \"CurrentThread host registration was not reserved or was already consumed\",\n    ))\n  }\n}\n\nfn release_host_registration_id(id: u64) {\n  RESERVED_HOST_REGISTRATIONS\n    .lock()\n    .unwrap_or_else(std::sync::PoisonError::into_inner)\n    .remove(&id);\n}\n\n#[napi]\n/// Reserve an exact CurrentThread host registration capability before either\n/// task or timer installation performs side effects. The JavaScript package\n/// validates the returned words and passes them back to one registration call.\npub fn reserve_current_thread_host_registration() -> napi::Result<BindingHostRegistration> {","sourceCodeStart":324,"sourceCodeEnd":360,"githubUrl":"https://github.com/napi-rs/napi-rs/blob/39bd1205e480a453a2da2601a760bde5a71ed016/crates/async-runtime/src/adapter.rs#L324-L360","documentation":"`claim_host_registration_id` verifies that the (high, low) word pair passed from JavaScript corresponds to an id previously returned by `reserve_current_thread_host_registration` and still present in `RESERVED_HOST_REGISTRATIONS`; if the id was never reserved or was already removed (each reservation is single-use — claimed on registration), it raises this `InvalidArg` error (crates/async-runtime/src/adapter.rs:342). It is a one-time-use token check guarding task and timer host installation.","triggerScenarios":"Calling `registerCurrentThreadTaskHost` or `registerTimerHost` with a registration that was never obtained from `reserveCurrentThreadHostRegistration`, with fabricated/zero high-low words, or with a registration already consumed by a previous successful registration call.","commonSituations":"Reusing a registration token to install a second task/timer; caching the registration across attempts and retrying after a failed installation; skipping the reserve step and passing arbitrary {high, low} values; wiring mistakes where the reserved token belongs to a different thread's host.","solutions":["Reserve a fresh token immediately before each registration: `const reg = reserveCurrentThreadHostRegistration()` then pass exactly its high/low once.","Never reuse a registration object after a successful `registerCurrentThreadTaskHost`/`registerTimerHost` call — request a new one.","Verify the high/low values are passed unmodified (no re-serialization or partial copies) from the reserve call to the registration call.","If an installation call failed midway, discard the token and reserve a new one instead of retrying with the same words."],"exampleFix":"// before (token reused)\nconst reg = reserveCurrentThreadHostRegistration();\nregisterCurrentThreadTaskHost(env, reg.high, reg.low, host);\nregisterTimerHost(env, reg.high, reg.low, timerHost); // InvalidArg\n\n// after\nconst reg1 = reserveCurrentThreadHostRegistration();\nregisterCurrentThreadTaskHost(env, reg1.high, reg1.low, host);\nconst reg2 = reserveCurrentThreadHostRegistration();\nregisterTimerHost(env, reg2.high, reg2.low, timerHost);","handlingStrategy":"try-catch","validationCode":"if (typeof reg !== 'object' || reg === null ||\n    typeof reg.high !== 'number' || typeof reg.low !== 'number') {\n  throw new TypeError('registration must be { high: number, low: number } from reserveCurrentThreadHostRegistration');\n}","typeGuard":"function isValidRegistration(reg): reg is { high: number, low: number } {\n  return typeof reg === 'object' && reg !== null &&\n    Number.isInteger(reg.high) && Number.isInteger(reg.low);\n}","tryCatchPattern":"try {\n  registerCurrentThreadTaskHost(env, reg.high, reg.low, host);\n} catch (e) {\n  if (e.message.includes('not reserved or was already consumed')) {\n    const fresh = reserveCurrentThreadHostRegistration(); // re-reserve once, do not loop\n  } else { throw e; }\n}","preventionTips":["Always reserve immediately before registering; treat the token as single-use.","Never retry a failed registration with the same token — reserve a new one.","Pass high/low through unmodified; avoid cloning through serialization.","Keep reserve/claim pairs on the same thread and in the same code path."],"tags":["invalid-argument","one-time-token","async-runtime","registration"],"backgroundTag":"invalid-argument-value","analyzedSha":"39bd1205e480a453a2da2601a760bde5a71ed016","analyzedAt":"2026-09-13T20:31:47.814Z","contentChangedAt":"2026-09-13T20:31:47.814Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}