heygen-com/hyperframes · error · HyperframesApiError

Invalid JSON response: ${(err as Error).message}

Error message

Invalid JSON response: ${(err as Error).message}

What it means

Thrown by the generated cloud API client when res.text() succeeds but JSON.parse(text) throws. The error is wrapped in HyperframesApiError carrying the HTTP status, the parse error message, and the first 500 chars of the raw body, so callers can distinguish a transport/encoding problem from a genuine API error envelope.

Source

Thrown at packages/cli/src/cloud/_gen/client.ts:141

    if (!res.ok) {
      throw await this.toApiError(res);
    }

    // 204 No Content
    if (res.status === 204) {
      return undefined as T;
    }

    const text = await res.text();
    if (!text) {
      return undefined as T;
    }
    let parsed: unknown;
    try {
      parsed = JSON.parse(text);
    } catch (err) {
      throw new HyperframesApiError({
        status: res.status,
        message: `Invalid JSON response: ${(err as Error).message}`,
        raw: text.slice(0, 500),
      });
    }
    // The /v3 envelope is {data: T, ...}. Unwrap when present and the
    // call site asked for it (the default) so consumers read the inner
    // payload directly. List endpoints opt out so they can read
    // ``has_more`` / ``next_token``.
    const unwrap = opts.unwrapData !== false;
    if (
      unwrap &&
      parsed &&
      typeof parsed === "object" &&
      "data" in (parsed as Record<string, unknown>)
    ) {
      const envelope = parsed as { data: T };
      return envelope.data;

View on GitHub (pinned to c2996c8626)

Solutions

  1. Inspect HyperframesApiError.raw (first 500 chars) and .status — if it is HTML from a proxy/gateway, fix the network egress or base URL.
  2. Verify the configured API base URL / registry URL is correct and reachable with `curl -i <url>`.
  3. If the body has a leading BOM or junk, ensure no transforming middleware sits between the client and server.
  4. Retry once on 502/504-style HTML in case of a transient gateway blip, then escalate to the API owner with the raw snippet.

Example fix

try {
  await client.listRenders();
} catch (err) {
  if (err instanceof HyperframesApiError && err.raw?.startsWith("<")) {
    // HTML body => gateway/proxy interception, not an API payload
    throw new Error(`API base URL misconfigured or blocked by a proxy: ${err.raw.slice(0, 120)}`);
  }
  throw err;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-send: sanity-check the base URL returns JSON
async function assertJsonEndpoint(baseUrl: string) {
  const res = await fetch(baseUrl + '/health');
  const ct = res.headers.get('content-type') ?? '';
  if (!ct.includes('application/json')) {
    throw new Error(`Base URL ${baseUrl} does not serve JSON (got ${ct})`);
  }
}

Type guard

function isHyperframesApiError(err: unknown): err is HyperframesApiError {
  return err instanceof Error && err.name === 'HyperframesApiError';
}

Try / catch

try {
  const data = await client.listRenders();
} catch (err) {
  if (err instanceof HyperframesApiError && err.raw?.startsWith('<')) {
    // HTML body => wrong base URL or proxy interception
  }
  throw err;
}

Prevention

When it happens

Trigger: Any HyperframesCloudClient request whose response is not valid JSON: an HTML 502/504 from a gateway, a proxy interception page, a truncated body, a text/plain error from a misconfigured base URL, or a BOM/prefix before the JSON. A 204 or empty body is handled earlier and does not reach this branch.

Common situations: Corporate/MITM proxy injecting an HTML block page; pointing config.registry or the API base URL at the wrong host that returns HTML; transient 5xx served as HTML by a load balancer; an API version mismatch where the endpoint returns a non-JSON landing page.

Understand the failure class

Related errors


AI-assisted analysis of heygen-com/hyperframes@c2996c8626 (2026-08-12). Data as JSON: /api/errors/660ae51f909d28f2. Report an issue: GitHub.