Back

Cómo solucionar 'Unexpected token < in JSON at position 0'

Cómo solucionar 'Unexpected token < in JSON at position 0'

Llamas a response.json() y tu aplicación se bloquea con SyntaxError: Unexpected token '<' in JSON at position 0. El carácter < te indica exactamente qué sucedió: tu código esperaba JSON pero recibió HTML en su lugar.

Este error de análisis de JSON aparece constantemente en los stacks frontend modernos: fetch del navegador, Node.js, rutas API de Next.js y funciones serverless. Comprender por qué ocurre y cómo depurarlo rápidamente te ahorrará horas de frustración.

Puntos Clave

  • El error “Unexpected token <” significa que estás analizando HTML como JSON—el < proviene típicamente de <!DOCTYPE html> o una etiqueta HTML.
  • Las causas comunes incluyen URLs incorrectas, redirecciones de autenticación, errores del servidor que devuelven páginas HTML y encabezados Content-Type faltantes.
  • Depura verificando primero el código de estado HTTP, luego el encabezado Content-Type y finalmente el cuerpo de respuesta sin procesar usando response.text().
  • Construye wrappers defensivos de fetch que validen las respuestas antes de analizarlas para detectar estos problemas con mensajes de error claros.

Por qué tu API devuelve HTML en lugar de JSON

El error significa que JSON.parse() encontró un documento HTML donde esperaba JSON válido. El < en la posición 0 es típicamente el carácter de apertura de <!DOCTYPE html> o una etiqueta HTML.

Varios escenarios causan esta discrepancia de Content-Type:

URLs de endpoints incorrectas o mal escritas. Un error tipográfico en tu URL de fetch devuelve una página 404—que es HTML, no JSON.

Redirecciones de autenticación. Tokens expirados o encabezados de autenticación faltantes desencadenan redirecciones a páginas de inicio de sesión. Tu fetch recibe el HTML de la página de login.

Errores del servidor que devuelven páginas HTML de error. Un error 500 de tu API gateway o proveedor de nube a menudo devuelve una página HTML de error estilizada en lugar de una respuesta de error JSON.

Servidores de desarrollo que sirven HTML de respaldo para rutas desconocidas. Muchas SPAs devuelven la estructura HTML para rutas no coincidentes, aunque algunos servidores de desarrollo modernos devuelven payloads de error estructurados.

Encabezados Content-Type faltantes o incorrectos. Código del servidor que olvida establecer Content-Type: application/json puede establecer HTML por defecto.

Un flujo práctico de depuración para errores de Fetch API

Cuando falla el análisis de JSON, sigue esta secuencia para identificar la causa raíz:

Paso 1: Verifica el código de estado HTTP

Antes de analizar, verifica el estado de la respuesta. Un estado 4xx o 5xx a menudo indica que la respuesta no será JSON:

const response = await fetch('/api/data')

if (!response.ok) {
  console.error(`HTTP ${response.status}: ${response.statusText}`)
  const text = await response.text()
  console.error('Response body:', text.substring(0, 200))
  throw new Error(`Request failed with status ${response.status}`)
}

Paso 2: Valida el encabezado Content-Type

Verifica qué afirma el servidor estar enviando:

const contentType = response.headers.get('content-type')

if (!contentType || !contentType.includes('application/json')) {
  const text = await response.text()
  throw new Error(`Expected JSON, received: ${contentType}. Body: ${text.substring(0, 100)}`)
}

const data = await response.json()

Paso 3: Registra el cuerpo de respuesta sin procesar

Cuando falla el análisis, usa response.text() en lugar de response.json() para ver qué recibiste realmente:

async function fetchWithDebug(url) {
  const response = await fetch(url)
  const text = await response.text()
  
  try {
    return JSON.parse(text)
  } catch (error) {
    console.error('Failed to parse JSON. Raw response:', text.substring(0, 500))
    throw error
  }
}

Problemas comunes del mundo real

URLs base de API incorrectas en producción. Variables de entorno que apuntan a dominios incorrectos o barras diagonales finales faltantes causan 404s que devuelven HTML.

API gateways y CDNs interceptando solicitudes. Servicios como Cloudflare, AWS API Gateway o Vercel pueden devolver sus propias páginas HTML de error para límites de velocidad, timeouts o configuraciones incorrectas.

App Router de Next.js y redirecciones de middleware. El middleware o redirecciones de autenticación a menudo devuelven HTML, aunque algunas rutas de redirección emiten pequeños payloads JSON en su lugar.

Código del servidor sin encabezados JSON. Tu manejador de API devuelve datos pero olvida establecer el tipo de contenido de la respuesta:

// ❌ Content-Type faltante
res.send({ data: 'value' })

// ✅ Respuesta JSON explícita
res.json({ data: 'value' })

Problemas de CORS. Los navegadores bloquean solicitudes preflight fallidas antes de que se ejecute tu código, pero servidores o proxies mal configurados aún pueden devolver una página HTML de error que tu llamada fetch recibe.

Patrón defensivo de Fetch

Envuelve tus llamadas fetch con validación para detectar estos problemas tempranamente:

async function safeFetch(url, options = {}) {
  const response = await fetch(url, options)
  
  if (!response.ok) {
    const body = await response.text()
    throw new Error(`HTTP ${response.status}: ${body.substring(0, 100)}`)
  }
  
  const contentType = response.headers.get('content-type')
  if (!contentType?.includes('application/json')) {
    const body = await response.text()
    throw new Error(`Invalid content-type: ${contentType}`)
  }
  
  return response.json()
}

Conclusión

El error “Unexpected token <” siempre significa que estás analizando HTML como JSON. Depura verificando primero el código de estado, luego el encabezado Content-Type y finalmente el cuerpo de respuesta sin procesar. La mayoría de los casos se remontan a URLs incorrectas, redirecciones de autenticación o errores del servidor que devuelven páginas HTML. Construye wrappers defensivos de fetch que validen las respuestas antes de analizarlas para detectar estos problemas con mensajes de error claros.

Preguntas Frecuentes

Los entornos de producción a menudo tienen configuraciones diferentes. Verifica que tu variable de entorno de URL base de la API esté configurada correctamente, confirma que tus tokens de autenticación sean válidos y asegúrate de que cualquier proxy o CDN entre tu frontend y la API esté configurado adecuadamente. Los servidores de producción también pueden tener políticas CORS más estrictas que causan que las solicitudes fallen.

Sí. Envuelve tus llamadas fetch en una función defensiva que verifique el estado de la respuesta y el encabezado Content-Type antes de llamar a response.json(). Esto te permite manejar el error de manera elegante y mostrar un mensaje significativo a los usuarios en lugar de bloquear la aplicación. Siempre valida las respuestas antes de analizarlas.

Usa la pestaña Network de tu navegador para inspeccionar la respuesta real. Si la respuesta muestra contenido HTML con un estado 404 o 500, el servidor está devolviendo una página de error. Si el estado es 200 pero el contenido es HTML, verifica tu código del servidor para asegurarte de que establezca el encabezado Content-Type correcto y devuelva JSON.

El método response.json() intenta analizar el cuerpo de la respuesta como JSON. Si el cuerpo contiene HTML o cualquier contenido que no sea JSON, el análisis falla. El método response.text() simplemente devuelve la respuesta sin procesar como una cadena sin analizarla, por eso funciona independientemente del tipo de contenido. Usa text() para depurar y ver qué recibiste realmente.

Understand every bug

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay