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
- Pass the lease string through completely unmodified — treat it as an opaque token, never parse/rebuild it client-side.
- 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).
- If the desktop app was updated/restarted between grant issuance and use, request a fresh grant from the file-picker and retry.
- 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
- Never parse, rebuild, or re-serialize the lease client-side; pass it opaquely.
- Keep signer (Rust shell) and verifier (backend) on the same lease secret lifecycle — restart both together.
- Run automated tests that forward an unmodified dialog-issued grant end-to-end to catch transport mangling.
- Audit middleware for body re-encoding or character translation on the lease field.
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
- VirusTotal request failed after {max_attempts} attempt(s): {
- Non-GGUF diffusion loads are restricted to unsloth/* repos (
- base_repo is restricted to unsloth/* repos (or a local path)
- {_cn_fs.reason}
- this torch cannot register the pre-quant constructor allowli
AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15).
Data as JSON: /api/errors/001627f08f0883dc.
Report an issue: GitHub.