{"record":{"id":"69bc90a360d47e6e","repo":"unslothai/unsloth","slug":"native-path-grant-has-an-unsupported-file-type","errorCode":null,"errorMessage":"Native path grant has an unsupported file type.","messagePattern":"Native path grant has an unsupported file type\\.","errorType":"validation","errorClass":"NativePathLeaseError","httpStatus":400,"severity":"error","filePath":"studio/backend/utils/native_path_leases.py","lineNumber":221,"sourceCode":"        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:\n        raise NativePathLeaseError(\"Native path grant has the wrong path type.\")\n    suffixes = tuple(s.lower() for s in (allowed_suffixes or ()))\n    if suffixes and resolved.suffix.lower() not in suffixes:\n        raise NativePathLeaseError(\"Native path grant has an unsupported file type.\")\n\n    current_identity = _validate_current_stat(grant, identity_options)\n    if current_identity is not None:\n        grant = replace(grant, device_id = current_identity[0], file_id = current_identity[1])\n    _consume_nonce(str(payload[\"nonce\"]), grant.expires_at_ms)\n    _remember_native_path_for_redaction(str(resolved), grant.display_label)\n    return grant\n\n\ndef display_label_for_native_path(value: str | None) -> str | None:\n    if not value:\n        return value\n    with _REDACTION_LOCK:\n        return _NATIVE_PATH_LABELS.get(value, value)\n\n\ndef is_registered_native_path_label(path_value: str | None, label: str | None) -> bool:\n    if not path_value or not label:","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/utils/native_path_leases.py#L203-L239","documentation":"The caller passed allowed_suffixes and the resolved path's extension (lowercased) is not in that set. This is an extension-level file-type gate applied after signature verification — e.g. a GGUF import endpoint passes allowed_suffixes=('.gguf',) so a signed-but-wrong file type is rejected before any read.","triggerScenarios":"User picks a file with a different extension than the endpoint allows (e.g. .safetensors for a .gguf-only import); the file has no extension at all; double extensions where only the last suffix counts; or frontend filters on the dialog not matching the backend's allowed_suffixes list.","commonSituations":"Dialog file filters drifted out of sync with backend allowed_suffixes after a code change; users renaming files to force them through; case differences are handled (comparison is lowercased) but compound suffixes like .gguf.part or archive.gguf.tmp are not in the list.","solutions":["Have the user re-select a file whose extension matches what the endpoint accepts (e.g. .gguf).","Sync the native dialog's file filter with the backend's allowed_suffixes for that endpoint so mismatches can't be picked in the first place.","If a legitimate new format should be accepted, add its suffix to allowed_suffixes in the backend call site.","Check for trailing whitespace or compound extensions on the filename (only the final .suffix segment is compared)."],"exampleFix":"# before\nverify_native_path_lease(lease, operation=\"import_gguf\", allowed_suffixes=(\".gguf\",))\n# user selected weights.safetensors -> error\n\n# after (frontend restricts the dialog)\nconst lease = await pickNativeFile({ filters: [{ name: \"GGUF\", extensions: [\"gguf\"] }] });\nverify_native_path_lease(lease, operation=\"import_gguf\", allowed_suffixes=(\".gguf\",))","handlingStrategy":"validation","validationCode":"from pathlib import Path\n\nALLOWED = {\".gguf\"}\n\ndef suffix_ok(path_str: str) -> bool:\n    return Path(path_str).suffix.lower() in ALLOWED","typeGuard":null,"tryCatchPattern":"try:\n    grant = verify_native_path_lease(lease, operation=OP, allowed_suffixes=(\".gguf\",))\nexcept NativePathLeaseError as exc:\n    if \"unsupported file type\" in str(exc):\n        return error_response(400, \"Only .gguf files are supported here.\")\n    raise","preventionTips":["Set the native dialog's extension filter to the same set as allowed_suffixes.","Keep filter lists in one shared constant used by frontend and backend.","Validate the picked filename's suffix in the UI before enabling submit."],"tags":["file-type","validation","extension","file-picker"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}