{"record":{"id":"cc013e6c16e5ae5f","repo":"unslothai/unsloth","slug":"native-path-grant-no-longer-resolves-to-the-select","errorCode":null,"errorMessage":"Native path grant no longer resolves to the selected path.","messagePattern":"Native path grant no longer resolves to the selected path\\.","errorType":"validation","errorClass":"NativePathLeaseError","httpStatus":400,"severity":"error","filePath":"studio/backend/utils/native_path_leases.py","lineNumber":199,"sourceCode":"\n    payload = _decode_payload(payload_b64)\n    _validate_payload(payload, operation = operation, expected_kind = expected_kind)\n\n    path = Path(str(payload[\"canonical_path\"]))\n    _reject_network_or_device_path(path)\n    try:\n        signed_lstat = os.lstat(path)\n    except OSError as exc:\n        raise NativePathLeaseError(\"Native path is no longer accessible.\") from exc\n    if _stat_module.S_ISLNK(signed_lstat.st_mode):\n        raise NativePathLeaseError(\"Native path is no longer a regular file.\")\n    try:\n        resolved = path.resolve(strict = True)\n    except OSError as exc:\n        raise NativePathLeaseError(\"Native path is no longer accessible.\") from exc\n    _reject_network_or_device_path(resolved)\n    if not _same_native_path(resolved, path):\n        raise NativePathLeaseError(\"Native path grant no longer resolves to the selected path.\")\n\n    identity_options = _identity_options(payload)\n    grant = NativePathGrant(\n        operation = str(payload[\"operation\"]),\n        canonical_path = resolved,\n        path_kind = str(payload[\"path_kind\"]),\n        path_type = str(payload[\"path_type\"]),\n        source_kind = str(payload[\"source_kind\"]),\n        token_id_hash = str(payload[\"token_id_hash\"]),\n        display_label = str(payload.get(\"display_label\") or resolved.name),\n        expires_at_ms = _required_int(payload, \"expires_at_ms\"),\n        size_bytes = _optional_int(payload.get(\"size_bytes\")),\n        modified_ms = _optional_int(payload.get(\"modified_ms\")),\n        device_id = identity_options[0][0] if identity_options else None,\n        file_id = identity_options[0][1] if identity_options else None,\n    )\n\n    if expected_path_type and grant.path_type != expected_path_type:","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/utils/native_path_leases.py#L181-L217","documentation":"After strict resolution, the resolved path is not the same file as the originally signed path (samefile() fails and normalized string comparison fails too). The signed canonical_path must resolve to itself; if resolution lands on a different inode/path, the grant no longer names the file the user actually selected.","triggerScenarios":"The path involves symlinks or bind mounts such that resolving yields a different filesystem identity than the signed path; the file was replaced (deleted + recreated) between signing and verification so inode identity changed; case-insensitive filesystems where the signed path's casing no longer matches the on-disk entry; or a mount was remounted over the directory.","commonSituations":"Selecting files through symlinked home directories (e.g. /tmp -> /private/tmp on macOS) where the Rust signer and Python resolver disagree on canonical form; file-replacement by editors with atomic-write patterns (new inode); mount namespace differences between the Tauri shell and a containerized backend.","solutions":["Re-select the file so the grant is issued from the same environment/view that verifies it.","Ensure the backend and the Tauri shell see the same mount and symlink layout (don't run one inside a container and the other on the host).","If you maintain the Rust signing side, confirm it signs the fully resolved canonical path, matching pathlib's resolve(strict=True) semantics.","Avoid replacing the selected file (copy-in-place instead of delete+rename) while its grant is outstanding."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    grant = verify_native_path_lease(lease, operation=OP)\nexcept NativePathLeaseError as exc:\n    if \"no longer resolves\" in str(exc):\n        return error_response(409, \"File identity changed; please re-select the file.\")\n    raise","preventionTips":["Run signer and backend under the same mount namespace/user view.","Sign fully resolved canonical paths on the Rust side.","Prefer in-place edits over delete+rename for files with outstanding grants."],"tags":["filesystem","canonicalization","symlink","identity"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}