jackwener/OpenCLI · error · CommandExecutionError

${label} returned malformed JSON: ${err?.message ?? err}

Error message

${label} returned malformed JSON: ${err?.message ?? err}

What it means

Thrown by goproxyJson when the HTTP response body from proxy.golang.org cannot be parsed as JSON. The Go module proxy normally serves strict JSON documents (@latest, @v/<ver>.info), so a parse failure means the endpoint returned HTML (error page, block page, captive portal) or a truncated body instead. The library wraps the underlying SyntaxError in a CommandExecutionError with the offending label for context.

Source

Thrown at clis/goproxy/utils.js:90

        throw new EmptyResultError(label, `proxy.golang.org returned ${resp.status} for ${url}.`);
    }
    if (resp.status === 429) {
        throw new CommandExecutionError(`${label} returned HTTP 429 (rate limited)`);
    }
    if (!resp.ok) {
        throw new CommandExecutionError(`${label} returned HTTP ${resp.status}`);
    }
    return resp;
}

export async function goproxyJson(url, label) {
    const resp = await rawFetch(url, label);
    let body;
    try {
        body = await resp.json();
    }
    catch (err) {
        throw new CommandExecutionError(`${label} returned malformed JSON: ${err?.message ?? err}`);
    }
    return body;
}

export async function goproxyText(url, label) {
    const resp = await rawFetch(url, label);
    return resp.text();
}

// Sort version tags by their numeric components, newest first. Pre-release tags
// (anything after "-" that isn't a pure number) sort lower than the matching
// release. Returns a new sorted array; non-tag inputs are dropped.
export function sortVersionsDescending(versions) {
    return versions
        .filter((v) => typeof v === 'string' && VERSION_TAG.test(v))
        .map((v) => ({ v, parts: parseSemver(v) }))
        .sort((a, b) => compareParts(b.parts, a.parts))
        .map((entry) => entry.v);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-run the command to rule out a transient/truncated response
  2. Verify network access: curl the same URL and inspect whether the body is JSON or HTML
  3. Check for corporate proxies, VPNs, or captive portals intercepting traffic
  4. If using a custom GOPROXY endpoint, confirm it actually implements the GOPROXY JSON protocol

Example fix

// before
const data = await goproxyJson(url, 'goproxy latest');
// after
let data;
try { data = await goproxyJson(url, 'goproxy latest'); }
catch (e) { data = null; /* fall back or surface message */ }
Defensive patterns

Strategy: try-catch

Validate before calling

// Preflight: confirm the endpoint serves JSON
const probe = await fetch(url);
const ct = probe.headers.get('content-type') || '';
if (!ct.includes('json')) throw new Error('endpoint did not return JSON: ' + ct);

Type guard

function isJsonObject(v) { return v !== null && typeof v === 'object' && !Array.isArray(v); }

Try / catch

try {
  const body = await goproxyJson(url, 'goproxy latest');
} catch (err) {
  // err is CommandExecutionError mentioning 'returned malformed JSON'
  console.error('Proxy response was not JSON; check network/proxy interception.');
  // optionally retry once or inspect via fetch + resp.text()
}

Prevention

When it happens

Trigger: Calling goproxyJson(url, label) where fetch succeeded (2xx) but resp.json() throws: proxy returned an HTML error/interstitial page with 200 status, a captive portal or corporate proxy injected HTML, or the body was truncated mid-stream.

Common situations: Corporate proxies rewriting responses, DNS hijacking to a portal page, proxy.golang.org serving an unexpected maintenance page, network middleware corrupting the response, or pointing a custom command at a non-GOPROXY URL that returns HTML.

Understand the failure class

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/0e4a6f6794576a44. Report an issue: GitHub.