{"record":{"id":"7cdb5883180e7c6f","repo":"unslothai/unsloth","slug":"native-path-grant-is-missing-its-folder-identity","errorCode":null,"errorMessage":"Native path grant is missing its folder identity.","messagePattern":"Native path grant is missing its folder identity\\.","errorType":"exception","errorClass":"NativePathLeaseError","httpStatus":400,"severity":"error","filePath":"studio/backend/utils/native_path_leases.py","lineNumber":367,"sourceCode":"        raise NativePathLeaseError(\"Native path is no longer accessible.\") from exc\n    if _stat_module.S_ISLNK(st.st_mode):\n        raise NativePathLeaseError(\"Native path is no longer a regular file.\")\n    if grant.path_type == \"file\":\n        if not _stat_module.S_ISREG(st.st_mode):\n            raise NativePathLeaseError(\"Native path is no longer a regular file.\")\n    elif grant.path_type == \"directory\":\n        if not _stat_module.S_ISDIR(st.st_mode):\n            raise NativePathLeaseError(\"Native path is no longer a directory.\")\n    else:\n        raise NativePathLeaseError(\"Native path grant has an unsupported path type.\")\n\n    if grant.size_bytes is not None and st.st_size != grant.size_bytes:\n        raise NativePathLeaseError(\"Native path changed after it was selected.\")\n    current_modified_ms = int(st.st_mtime_ns // 1_000_000)\n    if grant.modified_ms is not None and current_modified_ms != grant.modified_ms:\n        raise NativePathLeaseError(\"Native path changed after it was selected.\")\n    if grant.path_kind == \"document-folder\" and not identity_options:\n        raise NativePathLeaseError(\"Native path grant is missing its folder identity.\")\n    current_identity = (st.st_dev, st.st_ino)\n    expected_identity = _runtime_identity(identity_options)\n    if expected_identity is not None and current_identity != expected_identity:\n        raise NativePathLeaseError(\"Native path changed after it was selected.\")\n    return current_identity if expected_identity is not None else None\n\n\ndef _consume_nonce(nonce: str, expires_at_ms: int) -> None:\n    now_ms = int(time.time() * 1000)\n    with _REPLAY_LOCK:\n        for key, expiry in list(_USED_NONCES.items()):\n            if expiry <= now_ms:\n                _USED_NONCES.pop(key, None)\n        if nonce in _USED_NONCES:\n            raise NativePathLeaseError(\"Native path grant was already used.\")\n        _USED_NONCES[nonce] = expires_at_ms\n\n","sourceCodeStart":349,"sourceCodeEnd":385,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/utils/native_path_leases.py#L349-L385","documentation":"The grant's path_kind is \"document-folder\" but the payload carries no device_id/file_id identity pairs, which are mandatory for folder grants. Folder identity (st_dev, st_ino pairs) is how the backend detects that a granted directory was replaced by a different directory. _validate_current_stat() refuses to proceed without it because it could not enforce identity continuity.","triggerScenarios":"verify_native_path_lease() with payload['path_kind'] == 'document-folder' and _identity_options(payload) returning (): the signer omitted device_id/file_id arrays for a folder grant, or emitted empty arrays; older signer versions that only pinned identity for files.","commonSituations":"Signer regression dropping the identity fields for folder kinds; a signer/backend version mismatch after partial update; hand-built test payloads copied from a file grant (which may omit identity).","solutions":["Decode the payload and confirm device_id/file_id are missing or empty for the document-folder grant.","Update the Rust signer so document-folder grants always embed the folder's (st_dev, st_ino) pairs.","Update backend and desktop shell together to avoid the contract mismatch.","Re-select the folder after the fix to mint a compliant grant."],"exampleFix":"// before (Rust signer)\nif kind == \"file\" { payload[\"device_id\"] = ids; payload[\"file_id\"] = ids2; } // folders skipped\n\n// after\n// always attach identity, including document-folder grants\npayload[\"device_id\"] = ids;\npayload[\"file_id\"] = ids2;","handlingStrategy":"type-guard","validationCode":"import base64, json\n\ndef folder_grant_has_identity(lease: str) -> bool:\n    payload = json.loads(base64.urlsafe_b64decode(lease.split('.')[0] + '=='))\n    if payload.get('path_kind') != 'document-folder':\n        return True\n    d, f = payload.get('device_id'), payload.get('file_id')\n    return isinstance(d, list) and isinstance(f, list) and len(d) == len(f) and len(d) > 0","typeGuard":"def has_folder_identity(payload: dict) -> bool:\n    if payload.get('path_kind') != 'document-folder':\n        return True\n    d, f = payload.get('device_id'), payload.get('file_id')\n    return (\n        isinstance(d, list) and isinstance(f, list)\n        and len(d) > 0 and len(d) == len(f)\n        and all(isinstance(x, int) for x in d + f)\n    )","tryCatchPattern":"try:\n    grant = verify_native_path_lease(lease, operation='read')\nexcept NativePathLeaseError as exc:\n    if 'missing its folder identity' in str(exc):\n        return respond(400, 'Desktop app update required: folder grant lacks identity; update and re-select.')\n    raise","preventionTips":["Always attach (device_id, file_id) pairs when signing document-folder grants.","Keep signer and backend payload schemas in a shared spec/test both sides validate.","Integration-test folder grants end-to-end, not just file grants."],"tags":["native-path-lease","contract","folder-identity","validation"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}