janhq/jan · error · Error

MLX API request failed with status ${response.status}: ${JSO

Error message

MLX API request failed with status ${response.status}: ${JSON.stringify(errorData)}

What it means

Thrown by chat() (non-streaming branch) when POST /v1/chat/completions returns non-OK. The JSON error body is captured (or null). Unlike the crash guards (errors 50/51), this means the request reached the server and was rejected — a 4xx/5xx with a real error payload.

Source

Thrown at extensions/mlx-extension/src/index.ts:403

      'Authorization': `Bearer ${sessionInfo.api_key}`,
    }

    const body = JSON.stringify(opts)

    if (opts.stream) {
      return this.handleStreamingResponse(url, headers, body, abortController)
    }

    const response = await fetch(url, {
      method: 'POST',
      headers,
      body,
      signal: abortController?.signal,
    })

    if (!response.ok) {
      const errorData = await response.json().catch(() => null)
      throw new Error(
        `MLX API request failed with status ${response.status}: ${JSON.stringify(errorData)}`
      )
    }

    const completionResponse = (await response.json()) as chatCompletion

    if (completionResponse.choices?.[0]?.finish_reason === 'length') {
      throw new Error(OUT_OF_CONTEXT_SIZE)
    }

    return completionResponse
  }

  private async *handleStreamingResponse(
    url: string,
    headers: HeadersInit,
    body: string,
    abortController?: AbortController

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Read the embedded errorData — the server's message pinpoints the cause (bad template, unknown field, OOM).
  2. Strip non-standard fields from opts before sending; match the OpenAI chat completions schema.
  3. Verify sessionInfo.api_key matches the running session.
  4. For 5xx, check server logs; a chat-template error may require a different model build.

Example fix

// before
if (!response.ok) {
  const errorData = await response.json().catch(() => null)
  throw new Error(`MLX API request failed with status ${response.status}: ${JSON.stringify(errorData)}`)
}

// after
if (!response.ok) {
  const errorData = await response.json().catch(() => null)
  throw new Error(`MLX chat ${response.status}: ${errorData?.error?.message ?? JSON.stringify(errorData)}`)
}
Defensive patterns

Strategy: try-catch

Validate before calling

import { z } from 'zod'
const ChatReq = z.object({
  model: z.string(),
  messages: z.array(z.object({ role: z.string(), content: z.any() })),
  max_tokens: z.number().optional(),
})
const safe = ChatReq.parse(opts) // strip unknown fields before send

Try / catch

try {
  return await engine.chat(opts, abort)
} catch (e) {
  const msg = String(e)
  if (/status 4\d\d/.test(msg)) {
    // client-side error: fix opts and retry, don't loop
    throw new Error(`Chat rejected: ${msg}`)
  }
  throw e
}

Prevention

When it happens

Trigger: Malformed chatCompletionRequest body the MLX server rejects (400); unknown model id (404); request payload exceeds server limits; server-side inference error (500) such as a bad chat template or tokenization failure; 401 from a wrong api_key.

Common situations: Custom request builder omits required fields; tools/functions format unsupported by the model's template; opts contains fields the MLX server doesn't recognize; api_key drift between session and request.

Related errors


AI-assisted analysis of janhq/jan@fad3f12a14 (2026-08-12). Data as JSON: /api/errors/7e61adca1554a66d. Report an issue: GitHub.