{"record":{"id":"6e87efd0047c6c91","repo":"can1357/oh-my-pi","slug":"malformed-npm-registry-response-for-pkg-missin","errorCode":null,"errorMessage":"Malformed npm registry response for ${pkg}: missing version","messagePattern":"Malformed npm registry response for (.+?): missing version","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/cli/update-cli.ts","lineNumber":789,"sourceCode":"\t} catch (err) {\n\t\tif (isTimeoutError(err)) {\n\t\t\tthrow new Error(`Timed out fetching release info for ${pkg} after ${Math.round(timeoutMs / 1000)}s`, {\n\t\t\t\tcause: err,\n\t\t\t});\n\t\t}\n\t\tif (isUnsupportedProxyError(err)) throw new Error(unsupportedProxyMessage(), { cause: err });\n\t\tthrow err;\n\t}\n\tif (!response.ok) {\n\t\tif (response.status === 404 && channel === \"canary\") {\n\t\t\tthrow new Error(`No canary release has been published for ${pkg} yet. Try \\`${APP_NAME} update --stable\\`.`);\n\t\t}\n\t\tthrow new Error(`Failed to fetch release info for ${pkg}: ${response.statusText}`);\n\t}\n\n\tconst data: unknown = await response.json();\n\tif (!isRecord(data) || typeof data.version !== \"string\") {\n\t\tthrow new Error(`Malformed npm registry response for ${pkg}: missing version`);\n\t}\n\treturn { version: data.version, manifest: data };\n}\n\n/**\n * Get the latest release info from the npm registry, following `omp.rename`\n * pointers ({@link resolveReleaseRename}) when the package has moved to a new\n * npm name. Version, dist, and install names all come from the final manifest\n * in the chain. Uses npm instead of GitHub API to avoid unauthenticated rate\n * limiting.\n */\nexport async function getLatestRelease(\n\toptions: { timeoutMs?: number; channel?: UpdateChannel } = {},\n): Promise<ReleaseInfo> {\n\tconst timeoutMs = options.timeoutMs ?? RELEASE_METADATA_TIMEOUT_MS;\n\tconst channel = options.channel ?? \"stable\";\n\tconst packages: ReleasePackages = { ...CURRENT_PACKAGES };\n\tconst visited = new Set([packages.pkg]);","sourceCodeStart":771,"sourceCodeEnd":807,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/cli/update-cli.ts#L771-L807","documentation":"Thrown when the npm registry responds 200 but the JSON body is not an object containing a string `version` field. The updater validates the parsed manifest shape before trusting it, since downstream logic (comparison, download URLs) depends on `version`.","triggerScenarios":"response.json() returns null, an array, or an object without a string version property — e.g. a captive portal/proxy returned an HTML login page with status 200, or a registry mirror returned an error envelope.","commonSituations":"Corporate proxies or Wi-Fi captive portals intercepting HTTPS with a 200 HTML page (less common but seen with TLS-intercepting appliances), misconfigured custom registry (npm_config_registry pointing at a non-registry endpoint), corrupted mirror.","solutions":["Check which registry is being used (echo $NPM_REGISTRY / npm config get registry) and reset to the official one","Disconnect from captive-portal Wi-Fi or complete portal login, then retry","curl the endpoint and inspect the JSON: curl -s https://registry.npmjs.org/<pkg>/latest","Bypass TLS-intercepting proxies or install their CA so the real registry response arrives"],"exampleFix":"// before: custom mirror returns {error: 'not found'}\nexport NPM_REGISTRY=https://internal-mirror.example/\nomp update\n// after: use the official registry\nunset NPM_REGISTRY\nomp update","handlingStrategy":"validation","validationCode":"const res = await fetch(`${registry}/<pkg>/latest`);\nconst data = await res.json();\nif (typeof data?.version !== \"string\") throw new Error(\"registry returned non-release payload; check registry/proxy config\");","typeGuard":"function isRegistryManifest(v: unknown): v is { version: string; [k: string]: unknown } {\n  return typeof v === \"object\" && v !== null && typeof (v as { version?: unknown }).version === \"string\";\n}","tryCatchPattern":"try {\n  await runUpdate();\n} catch (err) {\n  if (String(err?.message).includes(\"Malformed npm registry response\")) {\n    console.error(\"A proxy/captive portal likely replaced the response. Check registry config and network.\");\n    return;\n  }\n  throw err;\n}","preventionTips":["Point NPM_CONFIG_REGISTRY / npm registry at the official registry for updates","Complete captive-portal login before running updates","Bypass or correctly configure TLS-intercepting proxies","Sanity-check the endpoint with curl before scripted updates"],"tags":["npm-registry","validation","malformed-response","updater"],"backgroundTag":"unexpected-response-shape","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}