unslothai/unsloth · error · NativePathLeaseError

Native path grant has expired.

Error message

Native path grant has expired.

What it means

The signed native path grant's expires_at_ms is in the past relative to the backend's current clock, so the lease is no longer honored. Grants are short-lived capabilities bound to a file the user just picked in the desktop UI; the backend refuses to read a native path once the lease window has closed. This is a freshness guarantee, not a permanent entitlement. The check uses the backend process clock, so clock skew between the Tauri shell and the backend also matters.

Source

Thrown at studio/backend/utils/native_path_leases.py:332

        "expires_at_ms",
        "nonce",
    )
    missing = [key for key in required if key not in payload]
    if missing:
        raise NativePathLeaseError("Native path grant payload is missing required fields.")
    if _required_int(payload, "version") != 1:
        raise NativePathLeaseError("Native path grant version is unsupported.")
    if payload["operation"] != operation:
        raise NativePathLeaseError("Native path grant operation is invalid.")
    if expected_kind and payload["path_kind"] != expected_kind:
        raise NativePathLeaseError("Native path grant kind is invalid.")
    now_ms = int(time.time() * 1000)
    issued_at_ms = _required_int(payload, "issued_at_ms")
    expires_at_ms = _required_int(payload, "expires_at_ms")
    if issued_at_ms >= expires_at_ms:
        raise NativePathLeaseError("Native path grant timestamps are inconsistent.")
    if expires_at_ms <= now_ms:
        raise NativePathLeaseError("Native path grant has expired.")
    if issued_at_ms > now_ms + 30_000:
        raise NativePathLeaseError("Native path grant issue time is invalid.")
    for key in ("canonical_path", "nonce", "token_id_hash", "display_label"):
        raw = payload.get(key)
        if raw is None:
            continue
        if "\x00" in str(raw):
            raise NativePathLeaseError("Native path grant contains invalid characters.")


def _validate_current_stat(
    grant: NativePathGrant, identity_options: tuple[tuple[int, int], ...]
) -> tuple[int, int] | None:
    try:
        st = os.lstat(grant.canonical_path)
    except OSError as exc:
        raise NativePathLeaseError("Native path is no longer accessible.") from exc
    if _stat_module.S_ISLNK(st.st_mode):

View on GitHub (pinned to 203007d190)

Solutions

  1. Request a fresh grant: re-trigger the native file selection (or the frontend's grant-refresh endpoint) and resend the request immediately.
  2. Shorten the gap between selection and submission so the operation lands inside the grant TTL.
  3. If it reproduces consistently, compare the signer's clock and the backend's clock (date +%s%3N on both) and fix NTP/timezone or container clock skew.
  4. If a longer window is genuinely needed, raise the TTL in the Rust signer (and keep issued_at < expires).

Example fix

// before (frontend)
const lease = await pickNativeFile(path); // grant cached for the whole session
submitJob(lease); // fails hours later

// after
async function submitJob(path) {
  const lease = await refreshNativeGrant(path); // fresh, short-lived grant
  await api.post('/job', { lease });
}
Defensive patterns

Strategy: retry

Validate before calling

import base64, json, time

def grant_still_valid(lease: str, skew_ms: int = 0) -> bool:
    payload = json.loads(base64.urlsafe_b64decode(lease.split('.')[0] + '=='))
    return int(payload['expires_at_ms']) > int(time.time() * 1000) + skew_ms

Try / catch

try:
    grant = verify_native_path_lease(lease, operation='read')
except NativePathLeaseError as exc:
    if 'has expired' in str(exc):
        lease = request_fresh_grant()   # re-pick / refresh endpoint
        grant = verify_native_path_lease(lease, operation='read')
    else:
        raise

Prevention

When it happens

Trigger: verify_native_path_lease() is called with a lease whose expires_at_ms <= int(time.time()*1000). Typical when the user picked a file, then submitted the job minutes later; when the request was retried/queued past the TTL; or when the backend clock is ahead of the machine clock that signed the grant.

Common situations: Long dwell time on a form after a file-picker grant was issued; paginated/retried uploads replaying an old grant; a laptop resumed from sleep (clock jumped); NTP drift between processes or containers; debugging sessions where the grant was captured hours earlier.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/1431c9d80fb57df0. Report an issue: GitHub.