unslothai/unsloth · error · NativePathLeaseError

Native path grant no longer resolves to the selected path.

Error message

Native path grant no longer resolves to the selected path.

What it means

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.

Source

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

    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):
        raise NativePathLeaseError("Native path grant no longer resolves to the selected path.")

    identity_options = _identity_options(payload)
    grant = NativePathGrant(
        operation = str(payload["operation"]),
        canonical_path = resolved,
        path_kind = str(payload["path_kind"]),
        path_type = str(payload["path_type"]),
        source_kind = str(payload["source_kind"]),
        token_id_hash = str(payload["token_id_hash"]),
        display_label = str(payload.get("display_label") or resolved.name),
        expires_at_ms = _required_int(payload, "expires_at_ms"),
        size_bytes = _optional_int(payload.get("size_bytes")),
        modified_ms = _optional_int(payload.get("modified_ms")),
        device_id = identity_options[0][0] if identity_options else None,
        file_id = identity_options[0][1] if identity_options else None,
    )

    if expected_path_type and grant.path_type != expected_path_type:

View on GitHub (pinned to 203007d190)

Solutions

  1. Re-select the file so the grant is issued from the same environment/view that verifies it.
  2. 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).
  3. If you maintain the Rust signing side, confirm it signs the fully resolved canonical path, matching pathlib's resolve(strict=True) semantics.
  4. Avoid replacing the selected file (copy-in-place instead of delete+rename) while its grant is outstanding.
Defensive patterns

Strategy: try-catch

Try / catch

try:
    grant = verify_native_path_lease(lease, operation=OP)
except NativePathLeaseError as exc:
    if "no longer resolves" in str(exc):
        return error_response(409, "File identity changed; please re-select the file.")
    raise

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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