denoland/deno · warning

Deno.autoUpdate: latest.json is not valid JSON

Error message

Deno.autoUpdate: latest.json is not valid JSON

What it means

During a periodic auto-update check, Deno fetched <base>/latest.json and got an HTTP 200 response, but the body failed JSON.parse (cli/rt/desktop.rs:1037-1041). The runtime treats the manifest as unusable, warns, and skips this check round. The app stays on its current version; the next interval tick will retry.

Source

Thrown at cli/rt/desktop.rs:1110

      return;
    }}

    const base = url.replace(/\/$/, "");
    const te = new TextEncoder();

    const check = async () => {{
      try {{
        const resp = await fetch(base + "/latest.json", {{
          cache: "no-store",
          redirect: "error",
        }});
        if (!resp.ok) return;
        const manifestText = await resp.text();
        let manifest;
        try {{
          manifest = JSON.parse(manifestText);
        }} catch {{
          console.warn("Deno.autoUpdate: latest.json is not valid JSON");
          return;
        }}
        if (manifest.version === _version) return;

        if (publicKey) {{
          const sig = manifest.signature;
          if (typeof sig !== "string" || !sig) {{
            console.error(
              "Deno.autoUpdate: publicKey configured but manifest has no signature",
            );
            return;
          }}
          // Signature is computed over the manifest with the `signature` field
          // removed, serialized canonically. To avoid depending on a JCS
          // implementation, signers must put the signature on a top-level
          // `signature` field and include the rest of the manifest verbatim
          // under a `signed` field (string). We then verify over that string.
          const signed = manifest.signed;

View on GitHub (pinned to f7822238ca)

Solutions

  1. curl -f <base>/latest.json from a clean network and run the body through a JSON validator to see the exact parse error
  2. Fix the server so /latest.json serves real JSON with Content-Type: application/json (disable HTML fallback for that path)
  3. Republish latest.json as strict JSON: no comments, no trailing commas, no BOM
  4. Check any proxy/CDN in front of the update host for body rewriting or truncation

Example fix

# before: updates.example.com serves the SPA fallback for /myapp/latest.json
$ curl -s https://updates.example.com/myapp/latest.json
<html><body>404</body></html>   # 200 + HTML -> 'not valid JSON' warning

# after: strict JSON manifest
{"version":"1.2.0","patches":{"1.1.0":{"name":"1.1.0-to-1.2.0.patch","sha256":"ab..."}}}
Defensive patterns

Strategy: validation

Validate before calling

// CI: validate exactly what the app will fetch before you publish
const res = await fetch(`${BASE}/latest.json`);
if (!res.ok) throw new Error(`latest.json unreachable: ${res.status}`);
const manifest = JSON.parse(await res.text()); // throws if the host serves anything but strict JSON
if (typeof manifest?.version !== "string" || typeof manifest?.patches !== "object") {
  throw new Error("latest.json: missing version/patches");
}

Type guard

function isManifest(v: unknown): v is { version: string; patches: Record<string, { name: string; sha256: string }> } {
  if (typeof v !== "object" || v === null) return false;
  const m = v as { version?: unknown; patches?: unknown };
  return typeof m.version === "string" && typeof m.patches === "object" && m.patches !== null;
}

Prevention

When it happens

Trigger: The update server returns 200 with non-JSON content: an HTML error/SPA fallback page, a truncated body, a BOM-prefixed file, a JSONC file with comments/trailing commas, or proxy/CDN-injected markup. Note resp.ok was already true, so this is purely a body-parsing failure, and redirect: 'error' means a redirect would have failed earlier, not here.

Common situations: Static hosts configured to serve index.html for unknown paths; hand-edited latest.json with a trailing comma; CDN transformations applied to the update path; publishing latest.json as JSONC from the release script.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


AI-assisted analysis of denoland/deno@f7822238ca (2026-08-20). Data as JSON: /api/errors/925b2196b92ee5ea. Report an issue: GitHub.