{"record":{"id":"b0d431e7b139dc43","repo":"unslothai/unsloth","slug":"could-not-read-os-path-basename-str-target","errorCode":null,"errorMessage":"Could not read {os.path.basename(str(target))}","messagePattern":"Could not read (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":500,"severity":"error","filePath":"studio/backend/routes/models.py","lineNumber":1821,"sourceCode":"                \"browse-folders: rejected path %r (normalized=%s)\",\n                path,\n                requested_path,\n            )\n        raise\n\n    entries: list[BrowseEntry] = []\n    truncated = False\n    visited = 0\n    try:\n        it = target.iterdir()\n    except PermissionError:\n        raise HTTPException(\n            status_code = 403,\n            detail = f\"Permission denied reading {os.path.basename(str(target))}\",\n        )\n    except OSError as exc:\n        logger.warning(\"browse-folders: could not read %s: %s\", target, exc, exc_info = True)\n        raise HTTPException(\n            status_code = 500,\n            detail = f\"Could not read {os.path.basename(str(target))}\",\n        )\n\n    try:\n        for child in it:\n            # Bound by *visited*, not *appended*: a cap on len(entries) would never trigger in dirs\n            # full of files. Counting visits caps worst-case work at ``_BROWSE_ENTRY_CAP``.\n            visited += 1\n            if visited > _BROWSE_ENTRY_CAP:\n                truncated = True\n                break\n            try:\n                if not child.is_dir():\n                    continue\n            except OSError:\n                continue\n            name = child.name","sourceCodeStart":1803,"sourceCodeEnd":1839,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/routes/models.py#L1803-L1839","documentation":"Raised as a 500 by the browse-folders listing handler when target.iterdir() throws a non-permission OSError on the validated target — the directory enumeration itself failed at the OS level. The detailed exception is logged server-side (with exc_info); the client receives only a basename-scoped 'Could not read' message to avoid leaking paths or raw OS errors.","triggerScenarios":"GET browse-folders?path=<dir> when the directory sits on a disconnected mapped drive, stale NFS/SMB mount, failing disk, or was deleted between the resolve step and the iterdir call.","commonSituations":"Network share dropped while the browse dialog was open; external USB drive unplugged; disk I/O errors; TOCTOU deletion races.","solutions":["Read the backend log line 'browse-folders: could not read' for the underlying errno.","Restore the underlying storage (reconnect drive, remount share, reconnect USB) and retry.","If deleted, refresh the tree from the allowlist root and navigate again."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"import os\n\ndef readable_now(d: str) -> bool:\n    try:\n        next(iter(os.scandir(d)), None)\n        return True\n    except OSError:\n        return False\n\nif not readable_now(target_dir):\n    entries = load_cached_listing(target_dir)  # degrade gracefully\nelse:\n    entries = browse(target_dir)","typeGuard":null,"tryCatchPattern":"try:\n    entries = browse(dir)\nexcept HTTPError as e:\n    if e.response.status_code == 500 and 'Could not read' in e.response.json()['detail']:\n        entries = load_cached_listing(dir)\n        warn_user('Folder is temporarily unreadable (storage offline?) — showing cached contents.')\n    else: raise","preventionTips":["Prefer local/always-mounted storage for model folders.","Cache last-good listings client-side so transient mount failures degrade instead of erroring.","Correlate with backend logs ('could not read ... exc_info') to find the underlying errno."],"tags":["filesystem","os-error","network-share","browse","transient"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}