Ir al contenido

Errores

Los errores llegan siempre con el mismo cuerpo:

{
"statusCode": 400,
"message": "token has invalid format",
"error": "Bad Request"
}
CampoTipo
statusCodenumberRepite el código HTTP.
messagestringDescripción legible. Es lo único que distingue dos errores del mismo código.
errorstringEtiqueta del tipo de error. Puede no venir.
CódigoSignifica¿Reintentar?
200Todo bien. Puede traer sections vacío.—
304El contenido no cambió desde tu ETag. Sin cuerpo.No — usa lo que ya tenías.
400Petición mal formada: token con formato inválido o page fuera del vocabulario.No — hay que arreglar la URL.
401El token tiene formato válido pero no corresponde a ninguna tienda.No.
403Origen no permitido, o el pedido fue bloqueado en el borde.No.
404El token corresponde a una tienda que ya no está activa.No.
429Superaste el límite de peticiones.Sí, con espera.
500Error del servidor.Sí, con backoff.

Dos causas, y el message las distingue.

Token mal formado. Se rechaza por forma, antes de buscar nada:

{ "statusCode": 400, "message": "token has invalid format" }

También "token is required" si el segmento viene vacío.

page fuera del vocabulario. El mensaje te dice exactamente qué se aceptaba:

{ "statusCode": 400, "message": "page must be one of: home-page, category-page, product-page" }
{ "statusCode": 400, "message": "page must be one of: category, product" }

Que aparezcan dos listas distintas es la pista: el vocabulario válido depende de si la ruta lleva {targetId}. Está explicado en endpoints.

El token cumple el formato pero no existe.

{ "statusCode": 401, "message": "Invalid token" }

Típicamente: token de otra tienda, token viejo después de una rotación, o un error de copiado que igual pasó la validación de forma.

El pedido no llegó por un origen aceptado, o fue frenado en el borde antes del backend. Si te ocurre desde el navegador con un token que sabes que es correcto, revisa desde dónde estás llamando.

El token es válido y corresponde a una tienda, pero esa tienda ya no está activa: desinstaló la app o está suspendida.

{ "statusCode": 404, "message": "Not found" }

Superaste el límite de peticiones. El límite se aplica por tienda, no por IP: son 600 peticiones por minuto para todo el tráfico de esa tienda.

En uso normal desde un tema no lo verás: una visita hace una o dos llamadas. Si aparece, casi siempre es por uno de estos dos:

  • Una llamada por sección en vez de una por página. Comparte la promesa — ver recetas de fetch.
  • Un fetch dentro de un loop de render o de un useEffect sin dependencias.

La distinción que importa: 4xx no se reintenta, 5xx y 429 sí. Un 400 o un 401 fallarán igual las siguientes mil veces.

async function traerContenido(url, { intentos = 3 } = {}) {
for (let intento = 0; intento < intentos; intento++) {
const res = await fetch(url);
if (res.ok) return res.json();
// Reintentable: límite de peticiones o error del servidor.
if (res.status === 429 || res.status >= 500) {
if (intento === intentos - 1) break;
await new Promise((r) => setTimeout(r, 2 ** intento * 500));
continue;
}
// 400, 401, 403, 404: el reintento no cambia nada.
const { message } = await res.json().catch(() => ({}));
throw new Error(`Sectionfy ${res.status}: ${message ?? 'sin detalle'}`);
}
throw new Error('Sectionfy: se agotaron los reintentos');
}

El contenido de esta API es un complemento de la ficha de producto, no su esqueleto. Si falla, la tienda tiene que seguir funcionando:

try {
const bundle = await traerContenido(url);
renderizar(bundle);
} catch (error) {
// No mostrar nada es mejor que mostrar un cartel de error a un comprador.
console.warn('[sectionfy] contenido no disponible', error);
}

Nunca dejes que un fallo de esta API bloquee el botón de compra ni deje un esqueleto de carga colgado para siempre.