alibaba/page-agent · error · InvokeError

INVALID_RESPONSE

INVALID_RESPONSE

Error message

Response body is not valid JSON

What it means

The HTTP request succeeded but response.json() failed — the response body is not valid JSON. This usually means the baseURL actually points at an HTML page, a proxy error page, or an empty/SSE stream rather than a JSON API. The parse error is attached as cause.

Source

Thrown at packages/llms/src/OpenAIClient.ts:135

					InvokeErrorTypes.SERVER_ERROR,
					`Server error: ${errorMessage}`,
					errorData
				)
			}
			throw new InvokeError(
				InvokeErrorTypes.UNKNOWN,
				`HTTP ${response.status}: ${errorMessage}`,
				errorData
			)
		}

		// 4. Parse and validate response
		let data: any
		try {
			data = await response.json()
		} catch (error) {
			if ((error as any)?.name === 'AbortError') throw error
			throw new InvokeError(
				InvokeErrorTypes.INVALID_RESPONSE,
				'Response body is not valid JSON',
				error
			)
		}

		const choice = data.choices?.[0]
		if (!choice) {
			throw new InvokeError(InvokeErrorTypes.INVALID_SCHEMA, 'No choices in response', data)
		}

		// Check finish_reason
		switch (choice.finish_reason) {
			case 'tool_calls':
			case 'function_call': // gemini
			case 'stop': // some models use this even with tool calls
				break
			case 'length':

View on GitHub (pinned to d02db1ee7c)

Solutions

  1. curl the exact URL with a sample body and inspect Content-Type and the raw response
  2. Fix baseURL to the JSON chat/completions endpoint including the /v1 path
  3. If the endpoint only supports streaming, ensure the request is configured so the server returns a single JSON object (no stream:true)
  4. Check for proxies/CDNs injecting HTML pages

Example fix

// before
new OpenAIClient({ baseURL: 'https://openai.com' }) // returns HTML

// after
new OpenAIClient({ baseURL: 'https://api.openai.com/v1' })
Defensive patterns

Strategy: type-guard

Validate before calling

// confirm the endpoint returns JSON before wiring the client
const r = await fetch(cfg.baseURL + '/models', { headers: { Authorization: `Bearer ${cfg.apiKey}` } })
const ct = r.headers.get('content-type') ?? ''
if (!ct.includes('application/json')) throw new Error(`Expected JSON, got ${ct}`)

Type guard

const isInvalidJsonError = (e: unknown): e is InvokeError =>
  e instanceof InvokeError && e.code === 'INVALID_RESPONSE' && /not valid JSON/.test(e.message)

Try / catch

try {
  await client.invoke(req)
} catch (e) {
  if (isInvalidJsonError(e)) throw new Error(`baseURL likely points at a non-API page: ${cfg.baseURL}`)
  throw e
}

Prevention

When it happens

Trigger: invoke() where the server returns HTML (login page, 200-redirected captive portal, Cloudflare interstitial), plain text, or an empty body with 200; pointing baseURL at a UI page instead of the API path; a gateway returning a non-JSON maintenance page.

Common situations: baseURL pointed at the provider's website instead of the API endpoint; reverse proxy serving an HTML error/landing page with 200; streaming endpoints that return SSE when the client expects a single JSON body; server truncating the response.

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 alibaba/page-agent@d02db1ee7c (2026-08-28). Data as JSON: /api/errors/3ee331bfe682a10f. Report an issue: GitHub.