unslothai/unsloth · critical · NativePathLeaseError

Native path grant signature is invalid.

Error message

Native path grant signature is invalid.

What it means

The HMAC-SHA256 signature attached to the lease does not match the signature computed over the payload segment using the backend's lease secret. Because the frontend can see but not modify a grant without breaking the HMAC, a signature mismatch means the grant bytes were altered after issuance or were signed with a different secret.

Source

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

    *,
    operation: str,
    expected_kind: str | None = None,
    expected_path_type: str | None = None,
    allowed_suffixes: Iterable[str] | None = None,
) -> NativePathGrant:
    if not lease:
        raise NativePathLeaseError("Native path grant is required.")

    secret = _decode_secret()
    payload_b64, signature_b64 = _split_lease(lease)
    expected_signature = hmac.new(
        secret,
        payload_b64.encode("ascii"),
        hashlib.sha256,
    ).digest()
    supplied_signature = _b64decode(signature_b64)
    if not hmac.compare_digest(expected_signature, supplied_signature):
        raise NativePathLeaseError("Native path grant signature is invalid.")

    payload = _decode_payload(payload_b64)
    _validate_payload(payload, operation = operation, expected_kind = expected_kind)

    path = Path(str(payload["canonical_path"]))
    _reject_network_or_device_path(path)
    try:
        signed_lstat = os.lstat(path)
    except OSError as exc:
        raise NativePathLeaseError("Native path is no longer accessible.") from exc
    if _stat_module.S_ISLNK(signed_lstat.st_mode):
        raise NativePathLeaseError("Native path is no longer a regular file.")
    try:
        resolved = path.resolve(strict = True)
    except OSError as exc:
        raise NativePathLeaseError("Native path is no longer accessible.") from exc
    _reject_network_or_device_path(resolved)
    if not _same_native_path(resolved, path):

View on GitHub (pinned to 203007d190)

Solutions

  1. Pass the lease string through completely unmodified — treat it as an opaque token, never parse/rebuild it client-side.
  2. Verify the backend process inherited the same UNSLOTH_STUDIO_NATIVE_PATH_LEASE_SECRET value the Tauri shell set when signing (both come from the managed desktop launcher).
  3. If the desktop app was updated/restarted between grant issuance and use, request a fresh grant from the file-picker and retry.
  4. Ensure no middleware, logging layer, or URL-decoder strips or rewrites characters (especially '.' and base64url '-_' vs '+/' confusion) in the lease.

Example fix

// before (frontend rebuilding the lease — breaks HMAC)
const lease = btoa(JSON.stringify(payload)) + "." + oldSig;

# after (opaque passthrough)
const lease = await window.__native.selectFile(options); // Rust-issued token
await api.importModel({ lease });
Defensive patterns

Strategy: try-catch

Try / catch

try:
    grant = verify_native_path_lease(lease, operation=OP)
except NativePathLeaseError as exc:
    if "signature is invalid" in str(exc):
        log_security_event("lease_signature_mismatch", client_fingerprint)
        return error_response(403, "File grant could not be verified. Re-select the file.")
    raise

Prevention

When it happens

Trigger: Any tampering with the base64url payload segment (changing canonical_path, expiry, operation, etc.), truncating or editing the dotted lease string, using a grant signed by a different installation/session whose UNSLOTH_STUDIO_NATIVE_PATH_LEASE_SECRET differs, or a backend process whose env secret does not match the one the Tauri shell used at signing time.

Common situations: Backend restarted or respawned with a stale/different lease secret in its environment; a lease captured from another machine or a previous app session; manual reconstruction of the lease string in frontend code (e.g. JSON.stringify + custom base64 instead of passing the opaque token); proxy or middleware that re-encodes the request body and mangles the lease.

Related errors


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