{"record":{"id":"de7e852d9c07cc06","repo":"unslothai/unsloth","slug":"virustotal-hash-lookup-returned-a-malformed-body","errorCode":null,"errorMessage":"VirusTotal hash lookup returned a malformed body","messagePattern":"VirusTotal hash lookup returned a malformed body","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"scripts/virustotal_scan.py","lineNumber":470,"sourceCode":"        Doing this first is both a quota saving and a disclosure saving: a bundle that\n        VirusTotal already holds gains nothing from being uploaded again.\n        \"\"\"\n        status, payload = self.request(\n            \"GET\",\n            f\"{API_ROOT}/files/{sha256}\",\n            allow_status = (404,),\n            deadline = deadline,\n        )\n        if status == 404:\n            return None\n        if not isinstance(payload, dict):\n            # A 200 whose body did not parse (a proxy error page, a truncated read)\n            # proves nothing about whether VirusTotal holds this file. Returning None\n            # here would be indistinguishable from a 404 and would upload the bundle,\n            # which is the one outcome the lookup exists to avoid: an unnecessary\n            # disclosure of an unreleased build. Fail closed and let the caller\n            # record the asset as unavailable instead.\n            raise RuntimeError(\"VirusTotal hash lookup returned a malformed body\")\n        return payload\n\n    def upload(\n        self,\n        path: Path,\n        deadline: float | None = None,\n    ) -> str:\n        \"\"\"Upload via the large-file flow and return the analysis id.\n\n        Every desktop bundle is 41-46 MB, which is over the 32 MB cap on\n        `POST /files`, so the signed upload URL is the only path that works here.\n\n        Each signed URL is SINGLE USE, so the POST is issued with retries disabled.\n        Replaying one after, say, the response body failed to read would be rejected\n        no matter how many times we tried, and would report the asset as unavailable\n        while an analysis was in fact already running. Retrying instead means going\n        back for a fresh URL, which is what the loop below does.\n        \"\"\"","sourceCodeStart":452,"sourceCodeEnd":488,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/scripts/virustotal_scan.py#L452-L488","documentation":"Thrown in the load-failure rollback path (use-chat-model-runtime.ts:1574-1590) when a model switch failed AFTER the previous model had been unloaded, and the previous model was loaded from a native file-picker path whose one-time, expiring nativePathToken could not be consumed (consumeNativePathToken threw). Because the previous model can only be reloaded through that signed lease, automatic rollback is impossible and the user must re-select the file manually. This error replaces/masks the original load error.","triggerScenarios":"Load model B while model A (chosen via the native file picker) is resident; A gets unloaded; B's load fails; then consumeNativePathToken(A's token) throws because the token was already consumed by this attempt's earlier lease exchange or expired. The catch converts that into this 'please re-select the file' error.","commonSituations":"Switching from a locally-picked GGUF/safetensors file to another model that then fails to load (OOM, bad repo id); tokens expiring during long loads; a failed switch retried after the single-use token was spent.","solutions":["Re-select the previous local model file in the file picker and load it again — this mints a fresh native-path token.","Check the server logs for why the target model failed to load (that root cause started the rollback) and fix it before retrying the switch.","To avoid repeat occurrences, prefer catalog/indexed models over raw file picks for models you switch away from and back to.","Freeze/stop other GPU consumers first if the target failed with out-of-memory so the retry succeeds."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"function isNativeFilePick(nativePathToken: string | undefined): boolean {\n  return typeof nativePathToken === 'string' && nativePathToken.length > 0;\n}","typeGuard":"function isRollbackTokenError(e: unknown): e is Error {\n  return e instanceof Error && /please re-select the file/.test(e.message);\n}","tryCatchPattern":"try {\n  await loadModel(selection);\n} catch (error) {\n  if (isRollbackTokenError(error)) {\n    // manual fallback: prompt the user to re-pick the previous local file and load it\n    promptReSelectPreviousFile();\n  } else throw error;\n}","preventionTips":["Keep the file-picker path handy (recent files list) so recovery is one click.","Fix the root cause of the failed target load (VRAM, repo id) before switching away from a native-pick model.","Prefer indexed/catalog models when you expect to switch models frequently — their rollback needs no token."],"tags":["model-loading","rollback","native-path","file-picker","frontend"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}