janhq/jan · error

Malformed chunk

Error message

Malformed chunk

What it means

Thrown when an SSE line from the llama.cpp server starts with neither 'data: ' nor 'error: ', so the stream parser cannot classify the chunk. The comment in the source notes this should not normally happen; it guards against protocol violations.

Solutions

  1. Inspect the raw chunk contents (log trimmedLine) to see what the server actually sent.
  2. Check for proxies/load balancers altering the streaming response and bypass them.
  3. Verify the llama.cpp server version matches what the extension expects.
  4. Make the parser resilient: skip empty/comment lines (': keep-alive') instead of throwing.

Example fix

// before
} else {
  throw new Error('Malformed chunk')
}
// after
} else if (trimmedLine.length > 0 && !trimmedLine.startsWith(':')) {
  throw new Error(`Malformed chunk: ${trimmedLine.slice(0, 100)}`)
}
Defensive patterns

Strategy: validation

Validate before calling

function isSseLine(line: string): boolean {
  return line.startsWith('data: ') || line.startsWith('error: ') || line.startsWith(':') || line.length === 0
}

Type guard

null

Try / catch

try {
  for await (const chunk of stream) yield chunk
} catch (e) {
  if ((e as Error).message === 'Malformed chunk') {
    logRawStreamForDebugging()
    throw new Error('Incompatible llamacpp server or interfering proxy on SSE stream')
  }
  throw e
}

Prevention

When it happens

Trigger: The server emits an unexpected line in the SSE stream — non-standard keep-alive comments not filtered out, a proxy injecting HTML error pages into the stream, or a llama.cpp version changing its stream format.

Common situations: Reverse proxy or middleware intercepting the streaming response; mismatched server/extension versions with different SSE framing; corrupted stream due to network truncation combined with buffer mis-splitting.

Understand the failure class

Related errors


AI-assisted analysis of janhq/jan@7205d770c1 (2026-09-17). Data as JSON: /api/errors/8b5edfe9f25bde4e. Report an issue: GitHub.

Appendix: source

Thrown at extensions/llamacpp-extension/src/index.ts:2465

        // Process complete lines in the buffer
        const lines = buffer.split('\n')
        buffer = lines.pop() || '' // Keep the last incomplete line in the buffer

        for (const line of lines) {
          const trimmedLine = line.trim()
          if (!trimmedLine || trimmedLine === 'data: [DONE]') {
            continue
          }

          if (trimmedLine.startsWith('data: ')) {
            jsonStr = trimmedLine.slice(6)
          } else if (trimmedLine.startsWith('error: ')) {
            jsonStr = trimmedLine.slice(7)
            const error = JSON.parse(jsonStr)
            throw new Error(error.message)
          } else {
            // it should not normally reach here
            throw new Error('Malformed chunk')
          }
          try {
            const data = JSON.parse(jsonStr)
            const chunk = data as chatCompletionChunk

            yield chunk
          } catch (e) {
            logger.error('Error parsing JSON from stream or server error:', e)
            // re‑throw so the async iterator terminates with an error
            throw e
          }
        }
      }
    } finally {
      reader.releaseLock()
    }
  }

View on GitHub (pinned to 7205d770c1)