{"record":{"id":"4864cd2879b80065","repo":"unslothai/unsloth","slug":"native-path-grant-secret-is-invalid","errorCode":null,"errorMessage":"Native path grant secret is invalid.","messagePattern":"Native path grant secret is invalid\\.","errorType":"validation","errorClass":"NativePathLeaseError","httpStatus":400,"severity":"error","filePath":"studio/backend/utils/native_path_leases.py","lineNumber":272,"sourceCode":"\n\ndef _decode_secret() -> bytes:\n    global _CACHED_LEASE_SECRET\n    if _CACHED_LEASE_SECRET is not None:\n        return _CACHED_LEASE_SECRET\n    with _SECRET_INIT_LOCK:\n        if _CACHED_LEASE_SECRET is not None:\n            return _CACHED_LEASE_SECRET\n        with _NATIVE_PATH_ENV_LOCK:\n            encoded = os.environ.get(LEASE_SECRET_ENV)\n            if encoded is None and _SCRUB_SAVED_SECRET is not None:\n                encoded = _SCRUB_SAVED_SECRET\n        if not encoded:\n            raise NativePathLeaseError(\"Native path grants require the managed desktop backend.\")\n        try:\n            secret = _b64decode(encoded)\n        except Exception as exc:\n            raise NativePathLeaseError(\"Native path grant secret is invalid.\") from exc\n        if len(secret) < _MIN_LEASE_SECRET_BYTES:\n            raise NativePathLeaseError(\"Native path grant secret is invalid.\")\n        _CACHED_LEASE_SECRET = secret\n        return secret\n\n\ndef _split_lease(lease: str) -> tuple[str, str]:\n    if not isinstance(lease, str):\n        raise NativePathLeaseError(\"Native path grant has an invalid format.\")\n    try:\n        lease.encode(\"ascii\")\n    except UnicodeEncodeError as exc:\n        raise NativePathLeaseError(\"Native path grant has an invalid format.\") from exc\n    parts = lease.split(\".\")\n    if len(parts) != 2 or not parts[0] or not parts[1]:\n        raise NativePathLeaseError(\"Native path grant has an invalid format.\")\n    return parts[0], parts[1]\n","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/utils/native_path_leases.py#L254-L290","documentation":"The lease secret found in the environment is not valid base64url — _b64decode(encoded) raised before any HMAC use. The secret must be base64url-encoded bytes; anything else (raw ASCII, standard base64 with '+'/'/', typos, quoted values) fails here.","triggerScenarios":"Setting UNSLOTH_STUDIO_NATIVE_PATH_LEASE_SECRET to a raw random string instead of base64url; including quotes or whitespace/newlines from shell or .env file interpolation; using standard base64 alphabet characters ('+', '/') that urlsafe decoding rejects; or a truncated copy-paste of the encoded value.","commonSituations":"Hand-rolling dev environments with openssl rand without base64 encoding; .env files that wrap long values across lines; YAML/JSON config that JSON-escapes the value differently; copy-paste from terminal output that inserted line breaks.","solutions":["Regenerate the secret as base64url of at least 32 random bytes: openssl rand -base64 48 | tr '+/' '-_' | tr -d '=' and set that value.","Make the signing side (Rust shell/dev harness) use the exact same encoded string.","Remove surrounding quotes, whitespace, and newlines when setting the env var.","Note the decode is unpadded-tolerant but urlsafe-alphabet-only — avoid '+' and '/' characters."],"exampleFix":"# before\nexport UNSLOTH_STUDIO_NATIVE_PATH_LEASE_SECRET='not-base64!!'\n\n# after\nexport UNSLOTH_STUDIO_NATIVE_PATH_LEASE_SECRET=\"$(openssl rand -base64 48 | tr '+/' '-_' | tr -d '=')\"","handlingStrategy":"validation","validationCode":"import base64, os\n\ndef secret_env_ok() -> bool:\n    raw = os.environ.get(\"UNSLOTH_STUDIO_NATIVE_PATH_LEASE_SECRET\")\n    if not raw:\n        return False\n    try:\n        return len(base64.urlsafe_b64decode(raw + \"=\" * (-len(raw) % 4))) >= 32\n    except Exception:\n        return False","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Generate secrets with a fixed recipe (openssl rand -base64 48 | tr '+/' '-_' | tr -d '=').","Avoid .env files that wrap or quote long values; prefer direct env injection.","Add a startup assertion that the secret decodes to >=32 bytes so misconfig fails at boot, not mid-request."],"tags":["configuration","base64","environment","secrets"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}