jackwener/OpenCLI · error · CommandExecutionError

hf spaces returned malformed JSON: ${err?.message ?? err}

Error message

hf spaces returned malformed JSON: ${err?.message ?? err}

What it means

The response body from the Hugging Face Spaces API could not be parsed as JSON. The library assumes the endpoint returns a JSON array of spaces, so resp.json() throwing is wrapped in a CommandExecutionError with the parse failure message.

Source

Thrown at clis/hf/spaces.js:78

        }
        catch (err) {
            throw new CommandExecutionError(`hf spaces request failed: ${err?.message ?? err}`);
        }
        if (resp.status === 429) {
            throw new CommandExecutionError(
                'hf spaces returned HTTP 429 (rate limited)',
                'Hugging Face throttles unauthenticated traffic; wait a few seconds and retry.',
            );
        }
        if (!resp.ok) {
            throw new CommandExecutionError(`hf spaces failed: HTTP ${resp.status}`);
        }
        let data;
        try {
            data = await resp.json();
        }
        catch (err) {
            throw new CommandExecutionError(`hf spaces returned malformed JSON: ${err?.message ?? err}`);
        }
        const list = Array.isArray(data) ? data : [];
        if (!list.length) {
            throw new EmptyResultError('hf spaces', 'No matching spaces on huggingface.co.');
        }
        return list.slice(0, limit).map((s, i) => {
            const id = String(s.id ?? s._id ?? '');
            const slash = id.indexOf('/');
            const author = String(s.author ?? (slash > 0 ? id.slice(0, slash) : ''));
            const tags = Array.isArray(s.tags) ? s.tags.filter((t) => !String(t).startsWith('license:')).slice(0, 10).join(', ') : '';
            return {
                rank: i + 1,
                id,
                author,
                sdk: String(s.sdk ?? ''),
                likes: s.likes != null ? Number(s.likes) : null,
                tags,
                lastModified: String(s.lastModified ?? '').slice(0, 10),

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log resp.headers.get('content-type') and the raw body to see what was actually returned
  2. Verify the request reaches the real HF API (no proxy interference) with curl
  3. Retry; if consistent, check HF status page for API incidents
  4. Use Accept: application/json header (already sent) and confirm no middleware alters the response

Example fix

// before
data = await resp.json();
// after
const text = await resp.text();
try {
  data = JSON.parse(text);
} catch {
  throw new CommandExecutionError(`hf spaces returned malformed JSON: ${text.slice(0, 120)}`);
}
Defensive patterns

Strategy: type-guard

Validate before calling

// pre-flight content-type check on a probe request
const head = await fetch('https://huggingface.co/api/spaces?limit=1');
if (!(head.headers.get('content-type') || '').includes('application/json')) {
  console.warn('HF response is not JSON — check proxy/interception');
}

Type guard

function isJsonResponse(resp) {
  return (resp.headers.get('content-type') || '').includes('application/json');
}

Try / catch

try {
  await hfSpaces(query);
} catch (e) {
  if (String(e.message).includes('malformed JSON')) {
    // inspect raw payload / retry once — often transient proxy corruption
    return await withRetry(() => hfSpaces(query), 1);
  }
  throw e;
}

Prevention

When it happens

Trigger: resp.json() throws: the server returned HTML (error/login page), an empty body with a 2xx status, truncated responses, or a proxy's plain-text error page instead of JSON.

Common situations: Captive portals or proxies intercepting HTTPS and returning HTML; HF returning 200 with an empty body during partial incidents; middleware rewriting responses.

Understand the failure class

Related errors


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