{"record":{"id":"21c63fe3521d5e3b","repo":"unslothai/unsloth","slug":"path-not-valid-json-exc","errorCode":null,"errorMessage":"{path}: not valid JSON: {exc}","messagePattern":"(.+?): not valid JSON: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"scripts/check_new_install_scripts.py","lineNumber":123,"sourceCode":"            )\n            if lifecycle:\n                ver = entry.get(\"version\") or \"<unversioned>\"\n                seen[f\"{name}@{ver}\"] = name\n            _walk_v1(entry.get(\"dependencies\"), depth = depth + 1)\n\n    if version == 1 or \"dependencies\" in lock:\n        _walk_v1(lock.get(\"dependencies\") or {})\n\n    return seen\n\n\ndef _load_lockfile(path: Path) -> dict:\n    if not path.exists():\n        raise FileNotFoundError(f\"lockfile not found: {path}\")\n    try:\n        return json.loads(path.read_text(encoding = \"utf-8\"))\n    except json.JSONDecodeError as exc:\n        raise ValueError(f\"{path}: not valid JSON: {exc}\") from exc\n\n\n# Registry lookup for the postinstall command body (best-effort).\n\n\ndef _fetch_registry_scripts(name: str, version: str) -> dict[str, str] | None:\n    \"\"\"Return {hook: command} for lifecycle hooks in registry metadata; None on any error (never raises).\"\"\"\n    safe_name = urllib.parse.quote(name, safe = \"@/\")\n    url = f\"{REGISTRY_BASE}{safe_name}/{urllib.parse.quote(version)}\"\n    try:\n        with urllib.request.urlopen(url, timeout = REGISTRY_TIMEOUT_SECS) as resp:\n            body = resp.read()\n    except (urllib.error.URLError, OSError, ValueError, TimeoutError):\n        return None\n    try:\n        meta = json.loads(body)\n    except json.JSONDecodeError:\n        return None","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/scripts/check_new_install_scripts.py#L105-L141","documentation":"Thrown by streamResearchEvents (research-api.ts:144-208) when the POST to /api/chat/research-runs/{id}/events returns response.ok === true but response.body is null/undefined. A 200 response to a text/event-stream request must always carry a body; a null body means the fetch stack (service worker, polyfill, HTTP/1.0 proxy, or a mocked fetch) did not expose streaming, so the SSE reader cannot be constructed.","triggerScenarios":"POST /api/chat/research-runs/{id}/events?after=N with accept: text/event-stream succeeding at the HTTP layer but yielding no stream — e.g. a service worker or older browser fetch polyfill returning a synthesized Response without a body, a proxy that buffers the whole stream and closes it oddly, or test mocks that return new Response() with no body argument.","commonSituations":"Running the studio frontend under an aggressive PWA/service-worker cache; older Safari or embedded webviews with incomplete streaming fetch; e2e tests mocking fetch without a ReadableStream; corporate proxies that strip transfer-encoding.","solutions":["Hard-reload with service workers disabled (DevTools > Application > Bypass for network) to rule out a synthesized Response.","Test in a current Chrome/Firefox; if the error follows the browser, the fetch implementation lacks streaming support.","Check whether a proxy between client and backend buffers SSE; configure it to stream (disable response buffering for text/event-stream).","In tests, mock fetch with a Response whose body is a ReadableStream (e.g. new Response(sseChunks.join(''), {headers:{'content-type':'text/event-stream'}}))."],"exampleFix":"// before (test mock)\nfetchMock.mockResolvedValue(new Response());\n\n// after — give the mock a real streaming body\nfetchMock.mockResolvedValue(new Response('id:1\\nevent:status\\ndata:{\"run\":{}}\\n\\n', { headers: { 'content-type': 'text/event-stream' } }));","handlingStrategy":"type-guard","validationCode":"async function probeEventStreamSupport(): Promise<boolean> {\n  const res = await fetch('/api/health', { method: 'POST' });\n  return typeof res.body?.getReader === 'function';\n}","typeGuard":"function hasStreamBody(response: Response): response is Response & { body: ReadableStream<Uint8Array> } {\n  return response.body instanceof ReadableStream;\n}","tryCatchPattern":"try {\n  for await (const event of streamResearchEvents(id, after, signal)) { handle(event); }\n} catch (error) {\n  if (error instanceof Error && error.message === \"Research event stream returned no response body\") {\n    // fetch stack cannot stream: disable live research updates or reload without service worker\n  } else throw error;\n}","preventionTips":["Bypass service workers for /api/chat/research-runs/*/events requests.","In tests, always mock fetch with a Response built from a real stream/string body.","Feature-detect ReadableStream support before subscribing UI to SSE panels."],"tags":["sse","streaming","fetch","research","frontend"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}