janhq/jan · error · Error

MLX model appears to have crashed! Please reload!

Error message

MLX model appears to have crashed! Please reload!

What it means

Thrown by chat() when the MLX process is reported alive (is_mlx_process_running) but the GET /health fetch throws. The process exists yet its HTTP server is not responding — it is wedged, still initializing, or the health endpoint is unreachable. The extension unloads the model first, then throws so the caller reloads.

Source

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

    opts: chatCompletionRequest,
    abortController?: AbortController
  ): Promise<chatCompletion | AsyncIterable<chatCompletionChunk>> {
    const sessionInfo = await this.findSessionByModel(opts.model)
    if (!sessionInfo) {
      throw new Error(`No active MLX session found for model: ${opts.model}`)
    }

    // Check if the process is alive
    const isAlive = await invoke<boolean>('plugin:mlx|is_mlx_process_running', {
      pid: sessionInfo.pid,
    })

    if (isAlive) {
      try {
        await fetch(`http://localhost:${sessionInfo.port}/health`)
      } catch (e) {
        this.unload(sessionInfo.model_id)
        throw new Error('MLX model appears to have crashed! Please reload!')
      }
    } else {
      throw new Error('MLX model has crashed! Please reload!')
    }

    const baseUrl = `http://localhost:${sessionInfo.port}/v1`
    const url = `${baseUrl}/chat/completions`
    const headers = {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${sessionInfo.api_key}`,
    }

    const body = JSON.stringify(opts)

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

View on GitHub (pinned to fad3f12a14)

Solutions

  1. Retry chat() after a brief wait; the server may finish booting (the extension already unloaded, so reload first).
  2. Increase the load timeout / readiness wait so /health is live before chat is allowed.
  3. On persistent failure, fully reload the model via load() to spawn a fresh server process.
  4. Check GPU/system resources — a wedged server often follows memory pressure.

Example fix

// before
const completion = await engine.chat(opts)

// after
try {
  return await engine.chat(opts)
} catch (e) {
  if (/appears to have crashed/.test(String(e))) {
    await engine.load(opts.model) // reload then retry once
    return await engine.chat(opts)
  }
  throw e
}
Defensive patterns

Strategy: retry

Validate before calling

async function mlxHealthReady(port: number): Promise<boolean> {
  try { return (await fetch(`http://localhost:${port}/health`)).ok }
  catch { return false }
}

if (!(await mlxHealthReady(sessionInfo.port))) {
  throw new Error('MLX /health not ready; wait or reload')
}

Try / catch

try {
  return await engine.chat(opts, abort)
} catch (e) {
  if (/appears to have crashed/.test(String(e))) {
    await engine.load(opts.model) // extension already unloaded it
    return await engine.chat(opts, abort)
  }
  throw e
}

Prevention

When it happens

Trigger: MLX server process is up but still binding its port or initializing weights when /health is hit; the server hung after a GPU error but did not exit; port mismatch where the recorded port no longer belongs to the server; localhost resolution issue.

Common situations: chat() called immediately after load() returned but before the HTTP server was truly ready; transient Metal/GPU fault wedged the server; firewall blocks localhost loopback on the chosen port.

Related errors


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