unslothai/unsloth · error · NativePathLeaseError
Native path grant was already used.
Error message
Native path grant was already used.
What it means
The grant's nonce is already recorded in the process-wide _USED_NONCES replay cache, so this exact grant was redeemed before. Nonces are single-use: once verify_native_path_lease() accepts a grant, its nonce is stored until expiry, making byte-identical replay impossible. This is an anti-replay control for capabilities that are otherwise short-lived and bearer-style.
Source
Thrown at studio/backend/utils/native_path_leases.py:382
if grant.modified_ms is not None and current_modified_ms != grant.modified_ms:
raise NativePathLeaseError("Native path changed after it was selected.")
if grant.path_kind == "document-folder" and not identity_options:
raise NativePathLeaseError("Native path grant is missing its folder identity.")
current_identity = (st.st_dev, st.st_ino)
expected_identity = _runtime_identity(identity_options)
if expected_identity is not None and current_identity != expected_identity:
raise NativePathLeaseError("Native path changed after it was selected.")
return current_identity if expected_identity is not None else None
def _consume_nonce(nonce: str, expires_at_ms: int) -> None:
now_ms = int(time.time() * 1000)
with _REPLAY_LOCK:
for key, expiry in list(_USED_NONCES.items()):
if expiry <= now_ms:
_USED_NONCES.pop(key, None)
if nonce in _USED_NONCES:
raise NativePathLeaseError("Native path grant was already used.")
_USED_NONCES[nonce] = expires_at_ms
def _remember_native_path_for_redaction(path: str, display_label: str) -> None:
with _REDACTION_LOCK:
_NATIVE_PATH_LABELS[path] = display_label
if len(_NATIVE_PATH_LABELS) > _MAX_NATIVE_PATH_LABELS:
excess = len(_NATIVE_PATH_LABELS) - _MAX_NATIVE_PATH_LABELS
for stale_path in list(_NATIVE_PATH_LABELS.keys())[:excess]:
_NATIVE_PATH_LABELS.pop(stale_path, None)
if path in _NATIVE_PATH_REDACTIONS:
return
_NATIVE_PATH_REDACTIONS.append(path)
del _NATIVE_PATH_REDACTIONS[:-_MAX_NATIVE_PATH_REDACTIONS]
def _reject_network_or_device_path(path: Path) -> None:
text = str(path)View on GitHub (pinned to 203007d190)
Solutions
- Request a fresh grant (re-pick the file or call the grant-refresh flow) for every request; never cache and reuse a lease across calls.
- Make retries idempotent at a higher level: on this error, refresh the grant and retry once with the new lease.
- Check whether the first submission actually succeeded before retrying (query job status) to avoid duplicate work.
- Ensure concurrent workers each obtain their own grant instead of sharing one.
Example fix
# before
resp = api.post('/train', json={'lease': lease}) # retry on timeout reuses lease
# after
def submit(lease_getter):
for attempt in range(2):
try:
return api.post('/train', json={'lease': lease_getter()})
except NativePathLeaseError as e:
if 'already used' not in str(e) or attempt:
raise Defensive patterns
Strategy: retry
Try / catch
try:
grant = verify_native_path_lease(lease, operation='read')
except NativePathLeaseError as exc:
if 'already used' in str(exc):
lease = request_fresh_grant() # nonces are single-use by design
grant = verify_native_path_lease(lease, operation='read')
else:
raise Prevention
- One grant per request: fetch a fresh lease for every API call, never cache it.
- Before retrying a timed-out request, check whether the first attempt succeeded (nonce may be burned).
- Give concurrent workers their own grants instead of sharing one lease string.
When it happens
Trigger: Calling verify_native_path_lease() twice with the same lease string within its TTL: a frontend retrying a failed request after the grant was already consumed (nonce is consumed late in verification, so a failure after _consume_nonce still burns it); duplicate form submissions; a job queue retrying a task that carries the same grant; two concurrent requests sharing one grant.
Common situations: HTTP retry on network timeout where the first attempt actually succeeded; double-click/duplicate submission; retry logic in an SDK replaying the request body verbatim; load tests reusing captured grant strings.
Related errors
- VirusTotal upload failed after {attempts} attempt(s): {last_
- A transformers installation is replacing the latest sidecar;
- The diffusion engine changed while this load was starting. R
- ChatGPT credential update is busy. Please retry.
- Authorization callback was invalid or already used.
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/0d8a23e61c5d4d50.
Report an issue: GitHub.