neoclide/coc.nvim · error

Unsupported charset: ${encoding}

Error message

Unsupported charset: ${encoding}

What it means

parseCharset extracts the charset parameter from a Content-Type header and maps common aliases (iso-8859-1, utf-16, us-ascii, etc.) to Node Buffer encodings. If the resulting name is not a supported Buffer encoding (Buffer.isEncoding fails), it throws 'Unsupported charset: <encoding>' instead of producing mojibake or crashing later during decode.

Source

Thrown at src/model/fetch.ts:210

  if (!Number.isFinite(maxResponseSize) || maxResponseSize <= 0) throw new Error('maxResponseSize must be a positive finite number')
  opts.maxResponseSize = maxResponseSize
  return opts
}

/**
 * Extract and validate the charset parameter of a Content-Type header.
 * Handles quoted values (charset="utf-8") and trailing parameters
 * (charset=utf-8; format=flowed); throws for unsupported encodings.
 */
function parseCharset(contentType: string): BufferEncoding {
  let match = /;\s*charset\s*=\s*"?([^";\s]+)"?/i.exec(contentType)
  let encoding = match ? match[1] : 'utf8'
  let lower = encoding.toLowerCase()
  if (lower === 'iso-8859-1' || lower === 'latin-1') encoding = 'latin1'
  else if (lower === 'utf-16' || lower === 'utf-16le' || lower === 'ucs-2' || lower === 'ucs2') encoding = 'utf16le'
  else if (lower === 'us-ascii') encoding = 'ascii'
  if (!Buffer.isEncoding(encoding)) {
    throw new Error(`Unsupported charset: ${encoding}`)
  }
  return encoding
}

export function request(url: URL, data: any, opts: any, token?: CancellationToken, obj: any = {}): Promise<ResponseResult> {
  let mod = getRequestModule(url)
  return new Promise<ResponseResult>((resolve, reject) => {
    let timer: NodeJS.Timeout
    let settled = false
    let req: any
    let cancellation: { dispose(): void } | undefined
    const cleanup = (): void => {
      cancellation?.dispose()
      cancellation = undefined
      if (timer) clearTimeout(timer)
    }
    const succeed = (value: ResponseResult): void => {
      if (settled) return

View on GitHub (pinned to 50e974d969)

Solutions

  1. Fix the server/proxy to send a supported charset (utf-8, latin1, utf16le, ascii).
  2. Fetch the body as buffer (options.buffer) and decode it yourself with iconv-lite for non-Node encodings.
  3. If you control the request URL, prefer an endpoint that returns UTF-8.

Example fix

// before
await fetch(url) // Content-Type: text/html; charset=gb2312 -> throws
// after
const res = await fetch(url, { buffer: true })
const text = iconv.decode(Buffer.from(res.content), 'gb2312')
Defensive patterns

Strategy: try-catch

Validate before calling

const SUPPORTED = new Set(['utf8','utf-8','latin1','utf16le','ascii'])
function charsetSupported(contentType) {
  const m = /charset=([\w-]+)/i.exec(contentType || '')
  return !m || SUPPORTED.has(m[1].toLowerCase())
}

Type guard

function hasSupportedCharset(ct: string | undefined): boolean {
  const m = /charset=([^;]+)/i.exec(ct || '')
  if (!m) return true
  const enc = m[1].trim().replace(/['"]/g, '').toLowerCase()
  return Buffer.isEncoding(enc) || ['iso-8859-1','utf-16','us-ascii'].includes(enc)
}

Try / catch

try {
  return await fetch(url)
} catch (e) {
  if (/Unsupported charset/.test(e.message)) {
    // fetch the raw bytes and decode with iconv-lite instead
    return decodeWithIconv(url, 'gb2312')
  }
  throw e
}

Prevention

When it happens

Trigger: A server responds with Content-Type: text/html; charset=gb2312, charset=windows-1252, charset=iso-8859-2, or an empty/typo'd charset value like charset=''; the unmapped name fails Buffer.isEncoding.

Common situations: Legacy or non-English websites serving regional charsets (GBK, Shift_JIS, EUC-KR) without a BOM; misconfigured proxies injecting malformed charset parameters.

Related errors


AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31). Data as JSON: /api/errors/4950824f98688e03. Report an issue: GitHub.