can1357/oh-my-pi · error · Error

Malformed npm registry response for ${pkg}: missing version

Error message

Malformed npm registry response for ${pkg}: missing version

What it means

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`.

Source

Thrown at packages/coding-agent/src/cli/update-cli.ts:789

	} catch (err) {
		if (isTimeoutError(err)) {
			throw new Error(`Timed out fetching release info for ${pkg} after ${Math.round(timeoutMs / 1000)}s`, {
				cause: err,
			});
		}
		if (isUnsupportedProxyError(err)) throw new Error(unsupportedProxyMessage(), { cause: err });
		throw err;
	}
	if (!response.ok) {
		if (response.status === 404 && channel === "canary") {
			throw new Error(`No canary release has been published for ${pkg} yet. Try \`${APP_NAME} update --stable\`.`);
		}
		throw new Error(`Failed to fetch release info for ${pkg}: ${response.statusText}`);
	}

	const data: unknown = await response.json();
	if (!isRecord(data) || typeof data.version !== "string") {
		throw new Error(`Malformed npm registry response for ${pkg}: missing version`);
	}
	return { version: data.version, manifest: data };
}

/**
 * Get the latest release info from the npm registry, following `omp.rename`
 * pointers ({@link resolveReleaseRename}) when the package has moved to a new
 * npm name. Version, dist, and install names all come from the final manifest
 * in the chain. Uses npm instead of GitHub API to avoid unauthenticated rate
 * limiting.
 */
export async function getLatestRelease(
	options: { timeoutMs?: number; channel?: UpdateChannel } = {},
): Promise<ReleaseInfo> {
	const timeoutMs = options.timeoutMs ?? RELEASE_METADATA_TIMEOUT_MS;
	const channel = options.channel ?? "stable";
	const packages: ReleasePackages = { ...CURRENT_PACKAGES };
	const visited = new Set([packages.pkg]);

View on GitHub (pinned to 9690622007)

Solutions

  1. Check which registry is being used (echo $NPM_REGISTRY / npm config get registry) and reset to the official one
  2. Disconnect from captive-portal Wi-Fi or complete portal login, then retry
  3. curl the endpoint and inspect the JSON: curl -s https://registry.npmjs.org/<pkg>/latest
  4. Bypass TLS-intercepting proxies or install their CA so the real registry response arrives

Example fix

// before: custom mirror returns {error: 'not found'}
export NPM_REGISTRY=https://internal-mirror.example/
omp update
// after: use the official registry
unset NPM_REGISTRY
omp update
Defensive patterns

Strategy: validation

Validate before calling

const res = await fetch(`${registry}/<pkg>/latest`);
const data = await res.json();
if (typeof data?.version !== "string") throw new Error("registry returned non-release payload; check registry/proxy config");

Type guard

function isRegistryManifest(v: unknown): v is { version: string; [k: string]: unknown } {
  return typeof v === "object" && v !== null && typeof (v as { version?: unknown }).version === "string";
}

Try / catch

try {
  await runUpdate();
} catch (err) {
  if (String(err?.message).includes("Malformed npm registry response")) {
    console.error("A proxy/captive portal likely replaced the response. Check registry config and network.");
    return;
  }
  throw err;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/6e87efd0047c6c91. Report an issue: GitHub.