jackwener/OpenCLI · error · CommandExecutionError

DuckDuckGo suggest returned malformed JSON

Error message

DuckDuckGo suggest returned malformed JSON: ${err?.message ?? err}

What it means

When the suggest endpoint responds 2xx but resp.json() throws, the command wraps the parse failure in a CommandExecutionError with the message 'DuckDuckGo suggest returned malformed JSON'. This happens when the body is not the expected JSON array-of-arrays shape — typically HTML (block page), an empty body, or truncated output.

Solutions

  1. Log the raw response text on failure to see what was actually returned
  2. Reduce request rate and retry; add jittered backoff
  3. Verify no proxy/firewall is rewriting responses
  4. Catch CommandExecutionError and treat as 'no suggestions' for that keyword
  5. Update parsing if DuckDuckGo changes the /ac/ response format

Example fix

// before
const data = JSON.parse(rawBody); // throws generic SyntaxError
// after
try {
  const data = await resp.json();
} catch (err) {
  const body = await resp.text().catch(() => '');
  console.error('unexpected body:', body.slice(0, 200));
  return [];
}
Defensive patterns

Strategy: fallback

Validate before calling

// sanity check content type before parsing expectations
const ct = resp.headers.get('content-type') ?? '';
if (!ct.includes('json')) {
  console.warn('suggest returned non-JSON content-type:', ct);
}

Type guard

function isSuggestPayload(d) {
  return Array.isArray(d) && d.length > 1 && Array.isArray(d[1]) && d[1].every(p => typeof p === 'string');
}

Try / catch

try {
  return await ddgSuggest({ keyword });
} catch (err) {
  if (/malformed JSON/.test(err?.message ?? '')) return []; // degrade gracefully
  throw err;
}

Prevention

When it happens

Trigger: resp.json() rejects: DuckDuckGo served an HTML error/anti-bot page with 200, empty body, or corrupted/truncated response; a proxy intercepted the response.

Common situations: Anti-bot interstitials returning 200 + HTML; captive portals in hotels/airports; aggressive request rates causing degraded responses; middlebox/proxy rewriting the body.

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 jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/85e308a26b51a765. Report an issue: GitHub.

Appendix: source

Thrown at clis/duckduckgo/suggest.js:35

  columns: ['phrase'],
  func: async (kwargs) => {
    const limit = requireBoundedInteger(kwargs.limit, 8, 1, 20, '--limit');
    const keyword = encodeURIComponent(requireSearchQuery(kwargs.keyword));
    const url = `https://duckduckgo.com/ac/?q=${keyword}&type=list`;
    let resp;
    try {
      resp = await fetch(url);
    } catch (err) {
      throw new CommandExecutionError(`DuckDuckGo suggest request failed: ${err instanceof Error ? err.message : String(err)}`);
    }
    if (!resp.ok) {
      throw new CommandExecutionError(`DuckDuckGo suggest returned HTTP ${resp.status}`);
    }
    let data;
    try {
      data = await resp.json();
    } catch (err) {
      throw new CommandExecutionError(`DuckDuckGo suggest returned malformed JSON: ${err?.message ?? err}`);
    }
    const phrases = Array.isArray(data) && data.length > 1 && Array.isArray(data[1]) ? data[1] : [];
    return phrases
      .filter((phrase) => typeof phrase === 'string' && phrase.trim())
      .slice(0, limit)
      .map(function(p) { return { phrase: p }; });
  },
});

export const __test__ = { command };

View on GitHub (pinned to 49907e53dc)