Docker “invalid reference format” — el error tipográfico que te hace perder horas (y la solución)

¿Te fue útil?

“invalid reference format” es la forma que tiene Docker de decir: “No puedo analizar lo que tú crees que es un nombre de imagen.” No es un problema de red. No es un problema de autenticación. Generalmente ni siquiera es un problema de Docker.

Es un problema de cadenas. Pequeño. De esos que llegan a producción porque parecen correctos en una revisión de código y solo fallan cuando una variable está vacía, se cuela un espacio o una etiqueta es formateada por un paso de construcción bienintencionado.

Qué significa realmente el error (y por qué es tan poco útil)

Docker lanza “invalid reference format” cuando intenta analizar una referencia de imagen (o a veces un nombre de contenedor, pero abrumadoramente suele ser la imagen) y la cadena no coincide con lo que Docker considera una referencia válida.

El problema es que Docker muestra el error en el nivel de abstracción equivocado. Tú piensas: “Estoy ejecutando un contenedor.” Docker piensa: “Estoy analizando una referencia según una gramática.” Esa gramática es estricta y, en algunos puntos, sorprendentemente exigente:

  • Los componentes del repositorio son, en su mayoría, en minúsculas.
  • Las etiquetas tienen sus propias reglas de caracteres y límites de longitud.
  • Los digests usan una sintaxis totalmente distinta y no se mezclan a la ligera con etiquetas.
  • Los espacios en blanco no se “ignoran”. El espacio en blanco es una trampa.

Los culpables más comunes en sistemas reales:

  • Espacios ocultos (por copiar/pegar, formato YAML, finales de línea de Windows, o un script que hace echo con una nueva línea al final).
  • Variables vacías que colapsan dos separadores en algo ilegal (como myapp: o repo//image).
  • Mayúsculas en nombres de repositorio (Docker es exigente por diseño).
  • Dos puntos mal colocados cuando hay tanto puerto del registro como etiqueta.
  • Comillas “inteligentes” y puntuación no ASCII provenientes de herramientas de chat o sistemas de tickets.

La mentalidad práctica: trata la referencia de imagen como una entrada que necesita validación y normalización, como una URL. Porque básicamente lo es—solo que con más equipaje y mensajes de error menos útiles.

Broma corta #1: Docker dice “invalid reference format” como una tostadora diría “error con el pan.” Técnicamente correcto, emocionalmente inútil.

Antes de ponernos tácticos, definamos qué entiende Docker por referencia. Una vez que comprendas eso, el 90% de estos incidentes dejarán de ser misteriosos y pasarán a ser vergonzosos de forma productiva.

La gramática real de una referencia de imagen

Las referencias de imagen de Docker no son cadenas libres. Siguen una estructura implementada por el analizador distribution/reference usado en todo el ecosistema (Docker Engine, registros, herramientas). Conceptualmente:

  • Nombre (opcionalmente con un registro y un namespace)
  • Etiqueta opcional introducida por :
  • Digest opcional introducido por @

El modelo mental que uso en respuesta a incidentes:

  • [registryhost[:port]/]path/name[:tag][@digest]
  • Donde path/name son uno o más componentes separados por barras.
  • Donde :tag y @digest son mutuamente excluyentes en muchos flujos (técnicamente puedes especificar ambos en algunos contextos, pero no lo hagas a menos que estés fijando deliberadamente y sepas cómo lo resuelve la herramienta).

Qué se permite en el nombre del repositorio

Los nombres de repositorio suelen requerir minúsculas y pueden incluir separadores como _, . y - en posiciones específicas. Las barras separan componentes de ruta. La regla más segura en producción es más simple que la especificación:

  • Usa letras minúsculas, números, guiones y barras para la ruta del repositorio.
  • No uses mayúsculas. No discutas esto. Perderás tiempo y dignidad.

Qué se permite en una etiqueta

Las etiquetas son más permisivas que los nombres de repositorio pero tampoco son arbitrarias. Los mayores errores que veo:

  • Incluir una barra en una etiqueta (feature/new-ui) porque los nombres de rama de Git la contienen.
  • Incluir espacios o caracteres de nueva línea, normalmente vía variables.
  • Empezar una etiqueta con un guion en algunos contextos de herramientas (no siempre ilegal, pero a menudo problemático cuando pasa por scripts).

Si necesitas mapear nombres de rama a etiquetas, sácalas seguro. No “esperes” que Docker lo acepte.

Hostnames de registro y puertos: la trampa del dos puntos

El dos puntos puede significar “puerto” o “etiqueta”, y Docker decide según el contexto:

  • registry.example.com:5000/team/app:1.2.3 es válido (puerto y luego etiqueta).
  • team/app:5000 también es válido (la etiqueta es 5000).
  • registry.example.com:team/app:1.2.3 no lo es (el puerto debe ser numérico).

Cuando veas “invalid reference format” y haya un dos puntos implicado, busca el caso de “dos dos puntos” y confirma cuál se supone que es el puerto.

Digests: imágenes fijadas, sintaxis distinta

Los digests se ven como @sha256:<hex>. Son excelentes para reproducibilidad y terribles para los humanos. Si los generas en pipelines, asegúrate de no introducir espacios en blanco o truncamiento. Un carácter faltante convierte un pin en un error de parseo.

Una cita que la gente de operaciones realmente vive

La esperanza no es una estrategia. — General Gordon R. Sullivan

Ese es el enfoque aquí. No “esperes” que tu etiqueta sea válida. Valídala. Normalízala. Y falla pronto con tu propio mensaje antes de que el analizador de Docker te deje en un hilo de tickets.

Datos interesantes y contexto histórico (porque las reglas no se inventaron ayer)

Son detalles pequeños, pero explican por qué las reglas de nombres se sienten extrañamente estrictas y por qué el mismo error aparece en distintas herramientas.

  1. El nombrado de imágenes precede a la popularidad actual de Docker. Las reglas evolucionaron junto a las primeras implementaciones de registros que necesitaban rutas deterministas y comportamiento de caché.
  2. El requisito de minúsculas es en parte una apuesta por compatibilidad. Los sistemas de archivos insensibles a mayúsculas (y los humanos) convierten los nombres con mayúsculas en un problema a largo plazo, así que el ecosistema apuesta por minúsculas.
  3. El analizador de “referencia” es compartido. Muchas herramientas dependen de la misma lógica subyacente, por eso “invalid reference format” aparece en Docker, Compose y otras herramientas de contenedores por las mismas causas raíz.
  4. Las etiquetas fueron pensadas como etiquetas ligeras, no como volcados de metadatos. Aún así la gente mete nombres de rama, marcas de tiempo y contexto de build en las etiquetas, y se sorprenden cuando se alcanzan límites o separadores ilegales.
  5. Los digests existen para resolver la “deriva de latest”. La sintaxis de digest ganó importancia cuando los equipos vieron que las etiquetas pueden moverse y “latest” es una trampa elegante.
  6. La sintaxis host + puerto del registro se inspiró en convenciones de URL. La ambigüedad del dos puntos (puerto vs etiqueta) es el impuesto inevitable por tener referencias legibles por humanos.
  7. Docker Hub influyó en los valores por defecto de nombres. La suposición de library/ para imágenes oficiales y registries por defecto moldeó cómo la gente escribe nombres cortos como nginx.
  8. Compose hizo el problema más visible. YAML introduce comillas, espacios y sustitución de variables—genial para configuración, excelente para ocultar errores tipográficos.
  9. Los pipelines de CI hicieron las etiquetas más dinámicas. Cuando las etiquetas dependen de variables de entorno, los fallos por cadena vacía se convirtieron en algo cotidiano.

Guía rápida de diagnóstico

Este es el orden que uso cuando me llaman. Está optimizado para “encontrar el cuello de botella rápido”, no por elegancia.

Primero: captura la cadena exacta que Docker está analizando

  • Copia el comando completo tal como se ejecutó, no lo que crees que ejecutaste.
  • Si está en CI, imprime el comando con variables expandidas (de forma segura) o haz echo solo de la referencia de imagen calculada.
  • Busca espacios en blanco, nuevas líneas y caracteres invisibles.

Segundo: identifica qué token es la referencia de imagen

  • En docker run, es el primer argumento no-flag después de las opciones.
  • En docker build, suele ser el valor de -t.
  • En Compose, está bajo image: o deriva de build: + el nombre del proyecto.

Tercero: reduce a un reproducer mínimo

  • Reemplaza variables por valores literales.
  • Quita todo excepto la referencia de imagen.
  • Prueba docker image inspect o docker pull con la misma referencia. Si el parseo falla, lo has aislado.

Cuarto: revisa estos culpables principales en orden

  1. Caracteres en mayúscula en la ruta del repositorio.
  2. Etiqueta que contiene una barra (/) o un espacio.
  3. Referencia que termina en : (etiqueta vacía).
  4. Separadores dobles: //, @@, :: en el lugar equivocado.
  5. Confusión host:port del registro (puerto no numérico, falta la barra después del host).
  6. Problemas de comillas en el shell y continuaciones de línea.

Si haces solo una cosa: imprime la referencia de imagen con una herramienta que revele caracteres invisibles. La mayoría de los casos “misteriosos” terminan justo ahí.

Tareas prácticas: comandos, salidas y decisiones (12+)

Estos pasos han sido probados en campo. Cada tarea incluye: un comando, qué significa la salida y la decisión que tomas. Ejecútalos localmente o en el runner de CI que falla. Úsalos para convertir mensajes vagos en una solución concreta.

Task 1: Reproducir el fallo de parseo con docker pull

cr0x@server:~$ docker pull 'MyTeam/MyApp:1.0.0'
invalid reference format

Qué significa: Docker rechazó la referencia antes de contactar cualquier registro. Las mayúsculas en la ruta del repositorio son un desencadenante común.

Decisión: Normaliza los nombres de repositorio a minúsculas en tus scripts de build/publish (myteam/myapp:1.0.0).

Task 2: Revelar espacios ocultos en una cadena de imagen calculada

cr0x@server:~$ IMAGE_REF="registry.local/team/app:${TAG} "
cr0x@server:~$ printf '%q\n' "$IMAGE_REF"
registry.local/team/app:${TAG}\ 

Qué significa: El espacio final es real. Docker lo tratará como parte de la referencia y fallará al parsearla.

Decisión: Limpia o sanea variables. No concatenes con entradas no confiables. Usa printf en lugar de echo en scripts.

Task 3: Confirmar que una variable está vacía (fallo clásico en CI)

cr0x@server:~$ TAG=""
cr0x@server:~$ docker build -t "team/app:${TAG}" .
invalid reference format

Qué significa: Una etiqueta vacía creó una referencia ilegal que termina en :.

Decisión: Falla pronto: exige ${TAG:?TAG must be set} o usa una etiqueta segura por defecto como dev.

Task 4: Mostrar cómo los nombres de rama rompen etiquetas

cr0x@server:~$ BRANCH="feature/new-ui"
cr0x@server:~$ docker tag alpine:3.20 "team/app:${BRANCH}"
Error parsing reference: "team/app:feature/new-ui" is not a valid repository/tag: invalid reference format

Qué significa: Las etiquetas no pueden contener barras. Las ramas de Git comúnmente sí.

Decisión: Sanea los nombres de rama (reemplaza / por -, elimina caracteres ilegales) antes de formar una etiqueta.

Task 5: Validar tu sanitización en el shell

cr0x@server:~$ BRANCH="feature/new-ui"
cr0x@server:~$ SAFE_TAG="$(printf '%s' "$BRANCH" | tr '[:upper:]' '[:lower:]' | sed 's#[^a-z0-9_.-]#-#g')"
cr0x@server:~$ printf '%s\n' "$SAFE_TAG"
feature-new-ui

Qué significa: Has transformado una etiqueta inválida en una que parece válida.

Decisión: Usa una función/biblioteca compartida para sanitizar etiquetas en todos los pipelines. No improvises en cada repositorio.

Task 6: Detectar contaminación CRLF de Windows (sí, pasa)

cr0x@server:~$ TAG="$(printf '1.2.3\r')"
cr0x@server:~$ printf '%q\n' "$TAG"
$'1.2.3\r'

Qué significa: Hay un carriage return en la etiqueta. Docker puede lanzar “invalid reference format” o comportarse de forma inconsistente según dónde aparezca.

Decisión: Elimina \r de variables y archivos en CI: sanea entradas provenientes de Git, herramientas de release y pasos basados en Windows.

Task 7: Distinguir puerto del registro vs uso del colon en la etiqueta

cr0x@server:~$ docker pull registry.local:5000/team/app:1.2.3
Error response from daemon: manifest for registry.local:5000/team/app:1.2.3 not found: manifest unknown

Qué significa: La referencia se parseó correctamente. Ya pasaste el formateo y estás en territorio de registro/contenido (manifest no encontrado).

Decisión: Deja de buscar errores tipográficos en el formato. Empieza a comprobar si la etiqueta existe en el registro.

Task 8: Provocar el modo de fallo “puerto no numérico”

cr0x@server:~$ docker pull registry.local:five000/team/app:1.2.3
invalid reference format

Qué significa: Docker intentó parsear registry.local:five000 como host:port y lo rechazó porque el puerto no es numérico.

Decisión: Audita el templating que construye los hostnames del registro. No interpolar “nombres de entorno” donde se espera un puerto.

Task 9: Confirmar la posición del token de referencia en docker run

cr0x@server:~$ docker run --rm -e FOO=bar 'team/app:1.0.0 '
Unable to find image 'team/app:1.0.0 ' locally
invalid reference format

Qué significa: La referencia incluía un espacio final. Docker la imprime tal cual y luego falla en el parseo/pull.

Decisión: Deja de copiar comandos de docs/chat formateados sin reescribir el último carácter. Además: entrecomilla variables, no cadenas compuestas con espacios desconocidos.

Task 10: Usar docker image inspect para separar “formato” de “no existe”

cr0x@server:~$ docker image inspect team/app:1.0.0
[]
Error: No such image: team/app:1.0.0

Qué significa: La referencia es válida, pero la imagen no está presente localmente.

Decisión: Si esperabas que estuviera local, arregla los pasos de build/pull. Si no, procede a docker pull y a las comprobaciones del registro.

Task 11: Confirmar que Compose interpoló lo que crees

cr0x@server:~$ cat docker-compose.yml
services:
  api:
    image: "registry.local/team/api:${TAG}"
cr0x@server:~$ TAG= docker compose config
services:
  api:
    image: registry.local/team/api:

Qué significa: Compose renderizó la imagen con una etiqueta vacía. Ese dos puntos final explotará después como “invalid reference format” o durante pull/run.

Decisión: Usa expansión por defecto en Compose: ${TAG:-dev} o exige variables requeridas en CI antes de invocar Compose.

Task 12: Capturar accidentes de comillas y nueva línea en YAML

cr0x@server:~$ python3 - <<'PY'
import os
ref = "registry.local/team/app:1.2.3\n"
print(repr(ref))
PY
'registry.local/team/app:1.2.3\n'

Qué significa: Las nuevas líneas pueden colarse cuando las cadenas provienen de archivos, plantillas o escalares de bloque YAML.

Decisión: Usa cadenas YAML de una sola línea para referencias de imagen. Evita escalares de bloque para cualquier cosa que se convierta en un token de línea de comandos.

Task 13: Confirmar que tu shell no dividió la referencia

cr0x@server:~$ REF="team/app:1.0.0"
cr0x@server:~$ set -x
cr0x@server:~$ docker run --rm $REF
+ docker run --rm team/app:1.0.0
Unable to find image 'team/app:1.0.0' locally
docker: Error response from daemon: pull access denied for team/app, repository does not exist or may require 'docker login': denied: requested access to the resource is denied.
See 'docker run --help'.

Qué significa: Esto no es un error de formato; es de autenticación/registro/disponibilidad.

Decisión: Deja de depurar sintaxis. Empieza a depurar credenciales, permisos del registro o la ruta correcta del repositorio.

Task 14: Validar una referencia por digest aparte de una etiqueta

cr0x@server:~$ docker pull alpine@sha256:deadbeef
invalid reference format

Qué significa: El digest está mal formado (demasiado corto, no hexadecimal, etc.). Docker lo rechaza como fallo de parseo de referencia.

Decisión: No escribas digests a mano. Cópialos exactamente y haz que las herramientas verifiquen la longitud/formato antes de usarlos.

Broma corta #2: Un espacio final en una etiqueta de imagen es como el glitter en casa: no lo verás, pero arruinará tu tarde.

Tres micro-historias del mundo corporativo

Micro-historia 1: El incidente causado por una suposición equivocada

El equipo había estandarizado las “etiquetas por rama” para entornos de preview. Las ramas de feature se construían en imágenes y se desplegaban en namespaces efímeros. A todo el mundo le encantaba—hasta un jueves por la tarde en que los deploys de preview empezaron a fallar en varios repositorios.

El on-call inicialmente lo trató como una caída del registro. El error en los logs: “invalid reference format.” Ese mensaje atrae al tipo equivocado de depuración, porque aparece junto a pulls y runs. Revisaron la salud del registro, rutas de red, DNS y tokens de autenticación. Todo parecía normal. Algunos builds seguían haciendo push de algo para ciertos servicios, pero no para otros.

La suposición equivocada: “Si falla durante el deploy, debe ser un problema del sistema de despliegue.” En realidad, el despliegue era el primer lugar donde la etiqueta mala se hacía visible. El cambio que lo provocó fue una actualización de la política del repositorio: los nombres de rama empezaron a incluir prefijos en mayúscula para tipos de trabajo. Piensa en Feature/New-UI y Hotfix/.... Git lo acepta. Las personas lo aceptan. Las etiquetas de Docker no aceptan la barra, y las rutas de repositorio no aceptan mayúsculas en muchos contextos.

El diagnóstico encajó cuando alguien imprimió la referencia calculada con escape visible y vio team/api:Feature/New-UI. La solución fue aburrida: sanear el nombre de la rama en una etiqueta segura y forzar minúsculas. También añadieron una barrera para que el pipeline fallara con un mensaje claro antes de invocar Docker.

La lección del postmortem no fue sobre Docker. Fue sobre suposiciones. Los sistemas fallan donde las restricciones se encuentran con la realidad. El nombrado de ramas en Git tiene restricciones distintas que las etiquetas de imagen, y “funcionó la semana pasada” no es un contrato.

Micro-historia 2: La optimización que salió mal

Un equipo de plataforma decidió acelerar el CI generando etiquetas de imagen a partir del ref completo de Git, más un SHA corto, más una marca de tiempo de build. El objetivo era trazabilidad sin tener que mirar nada. En papel: genial. En pipelines: una comedia a cámara lenta.

La optimización se implementó como una pequeña función de shell reutilizada en repos. Usaba echo abundantemente, tuberías con un par de utilidades de texto y terminaba con una nueva línea. Esa nueva línea no importaba al imprimir logs. Importaba mucho cuando se interpolaba en la etiqueta de docker build -t. Algunos runners la limpiaban, otros no. En ciertos shells se convertía en un espacio final o en una nueva línea literal según cómo se expandieran las variables.

La mayoría de builds pasaban. Luego un subconjunto empezó a fallar con “invalid reference format”, aparentemente al azar. Los desarrolladores culparon a versiones de Docker, imágenes de runner y al “CI inestable”. El on-call culpó a la luna.

La causa raíz fue que la “etiqueta de trazabilidad” ocasionalmente excedía restricciones y a menudo contenía caracteres ilegales copiados del formato del ref de Git. El formato de timestamp también incluía dos puntos, legales en algunas etiquetas pero arriesgados cuando se combinaban con otros pasos de parseo y formateo de logs. La nueva línea fue el insulto final.

El rollback fue inmediato. La sustitución fue más simple: las etiquetas pasaron a ser ${sanitized_branch}-${short_sha} con límites estrictos de longitud, y los metadatos ricos se movieron a labels (org.opencontainers.image.revision, org.opencontainers.image.source) donde pertenecen. La trazabilidad mejoró. El CI dejó de “romperse al azar”.

Micro-historia 3: La práctica aburrida pero correcta que salvó el día

Otra organización tenía una costumbre que parecía excesivamente cautelosa: cada paso de pipeline que calculaba una referencia de imagen la escribía en un archivo, luego la validaba con un pequeño script y luego la imprimía usando un formato amigable para escape. A nadie le entusiasmaban las líneas extra de código. Pero era consistente, probado y compartido.

Un día se añadió un servicio nuevo por un equipo que vivía mayormente en herramientas de Windows. Su pipeline inyectó una cadena de versión leída de un archivo que incluía finales CRLF. La etiqueta parecía correcta en los logs. Pero la cadena calculada contenía un \r oculto. Docker empezó a lanzar “invalid reference format”.

Aquí la práctica aburrida pagó su renta: el script validador marcó la etiqueta como conteniendo un carriage return y falló pronto con un mensaje que apuntaba al valor byte exacto y al archivo de origen. Sin depuración de registros. Sin agujeros de conejo de Compose. Sin “funciona en mi máquina”.

La solución fue un paso de normalización de una línea en el pipeline más una política de repositorio para asegurar que los archivos de versión usan LF. Nunca se convirtió en una caída. Se cerró como ticket con causa raíz clara y sistema endurecido.

La buena operación a menudo es solo negarse a dejar que cadenas ambiguas fluyan por tus sistemas sin control. No es glamoroso. Es efectivo.

Errores comunes: síntoma → causa raíz → solución

Esta sección sirve mientras miras logs. Cada entrada mapea un síntoma reconocible a la causa más probable y una solución específica.

1) Síntoma: “invalid reference format” al usar docker build -t

Causa raíz: La variable de la etiqueta está vacía o contiene espacios/nuevas líneas.

Solución: Exige variables requeridas y recorta entradas. Usa ${TAG:?TAG must be set} y genera etiquetas con printf en vez de echo.

2) Síntoma: Funciona localmente, falla en CI con el mismo comando aparentemente igual

Causa raíz: Las variables de CI contienen caracteres ocultos (\r, espacio final) o se interpolan de forma distinta en el shell.

Solución: Imprime la referencia computada con printf '%q\n'. Normaliza finales de línea y usa opciones estrictas de shell (set -euo pipefail).

3) Síntoma: “Error parsing reference … is not a valid repository/tag”

Causa raíz: La etiqueta contiene / (nombre de rama) o el repositorio contiene mayúsculas.

Solución: Sanea los nombres de rama para convertirlos en etiquetas; fuerza minúsculas en rutas de repositorio.

4) Síntoma: Invalid reference format con hostname de registro + puerto

Causa raíz: Puerto no numérico, falta la barra después del host o colon extra accidental.

Solución: Asegura host:port/path/name:tag. Mantén la construcción del host del registro separada de la construcción de la etiqueta.

5) Síntoma: Falla solo al copiar desde chat o tickets

Causa raíz: Comillas inteligentes, espacios de no separación o puntuación Unicode.

Solución: Vuelve a escribir las comillas manualmente; valida con python3 imprimiendo repr() o con printf '%q' en shell.

6) Síntoma: Compose dice invalid reference format, pero el YAML parece correcto

Causa raíz: La expansión de variables creó una etiqueta vacía, o un escalar de bloque YAML introdujo una nueva línea.

Solución: Ejecuta docker compose config e inspecciona la salida renderizada. Usa ${VAR:-default} o exige la presencia de variables.

7) Síntoma: Intentaste usar un digest y falla inmediatamente

Causa raíz: El digest está mal formado o truncado.

Solución: Copia el digest completo; añade comprobaciones del prefijo esperado sha256: y la longitud antes de usarlo.

8) Síntoma: El error aparece tras “optimizar” el etiquetado por trazabilidad

Causa raíz: La etiqueta incluye caracteres ilegales o es demasiado larga; se introdujo metadato en la etiqueta.

Solución: Pon los metadatos en labels OCI. Mantén las etiquetas cortas, saneadas y estables.

9) Síntoma: La referencia parece válida pero Docker aún se queja

Causa raíz: Espacio en blanco invisible, especialmente espacio final o nueva línea.

Solución: Imprime con escape y comprueba el contenido a nivel de bytes. Trata todas las cadenas como hostiles hasta que se demuestre lo contrario.

Listas de verificación / plan paso a paso

Lista A: Cuando ves “invalid reference format” en un incidente

  1. Extrae la cadena exacta de referencia de logs o de la salida del paso de CI. No la reescribas todavía.
  2. Revela invisibles: imprime con escape (printf '%q\n') o con representación (python3 -c usando repr).
  3. Confirma qué herramienta lo produjo (Docker CLI, Compose, sistema de build). Encuentra el punto donde se arma la referencia.
  4. Revisa variables por vacíos y valores por defecto. Las cadenas vacías son la causa #1 del fallo por dos puntos final.
  5. Escanea por caracteres ilegales: espacios, barras en etiquetas, mayúsculas en la ruta del repo, \r.
  6. Reduce a un reproducer mínimo: docker pull o docker image inspect con la referencia exacta.
  7. Arregla en la fuente: el paso de construcción de la cadena, no la invocación de Docker.
  8. Añade una validación preflight que falle con un mensaje humano antes de que lo haga Docker.

Lista B: Una forma robusta de generar etiquetas en CI

  1. Elige entradas: nombre de rama, SHA corto, número de build opcional.
  2. Todo en minúsculas (las etiquetas a veces aceptan mayúsculas, pero la coherencia gana).
  3. Reemplaza caracteres ilegales por -.
  4. Colapsa separadores repetidos (evita el desastre ---).
  5. Recorta separadores al inicio/fin.
  6. Aplica un límite de longitud sensato (por ejemplo 60–80 chars para evitar casos límite).
  7. Emite con printf y sin sorpresas de nueva línea.
  8. Almacena la referencia calculada en un archivo artefacto y reutilízala consistentemente.

Lista C: Controles preventivos que realmente recomiendo

  • Una librería de etiquetado compartida por organización (función de shell, objetivo Make, pequeño helper en Go/Python). Nada de lógica inventada por repos.
  • Comprobaciones fail-fast de variables antes de llamar a Docker o Compose.
  • Salida renderizada de Compose como artefacto para depuración (docker compose config).
  • Labels OCI para metadatos, no inflar etiquetas.
  • Linter de referencias de imagen en CI (incluso una regla regex es mejor que intuir).

FAQ

1) ¿“invalid reference format” alguna vez significa que el registro está caído?

Casi nunca. Normalmente falla antes de cualquier llamada de red. Si lo ves, asume primero parseo de cadena. Si la referencia se parsea, obtendrás errores como “manifest unknown”, “pull access denied” o fallos TLS/auth.

2) ¿Por qué Docker “odia” las mayúsculas?

Es cuestión de consistencia y compatibilidad entre sistemas de archivos, registros y herramientas. Los nombres con mayúsculas generan ambigüedad y dolor operativo. El ecosistema eligió la rigurosidad antes que el caos.

3) ¿Puede una etiqueta de Docker contener una barra?

No. La barra es un separador de camino en el nombre del repositorio. Si intentas poner una rama de Git como feature/foo en una etiqueta, obtendrás fallos de parseo. Sánala primero.

4) ¿Cuál es la diferencia práctica entre etiqueta y digest?

Una etiqueta es un puntero mutable (puede apuntar a otra imagen). Un digest es una dirección por contenido y es efectivamente inmutable. Usa digests para reproducibilidad, etiquetas para flujos de trabajo y navegación humana.

5) Compose muestra el error, pero la línea de imagen parece correcta. ¿Y ahora?

Ejecuta docker compose config. Compose puede estar interpolando variables en una etiqueta vacía o insertando espacios/nuevas líneas por el formato YAML. Depura la configuración renderizada, no el YAML fuente.

6) ¿Por qué funcionó ayer y falla hoy con el mismo pipeline?

Porque los inputs cambiaron: nombres de rama, cadenas de versión, variables de entorno, OS del runner o finales de línea. Tu pipeline es una fábrica de cadenas. Cualquier carácter nuevo puede convertirse en un incidente de producción.

7) ¿Cómo imprimo de forma segura una referencia para depuración sin filtrar secretos?

Las referencias de imagen no deberían contener secretos. Si las tuyas sí lo hacen (por ejemplo, incrustando credenciales en la cadena del registro), para y corrige esa arquitectura. Para depurar, imprime solo la referencia calculada y mantén credenciales en docker login o en helpers de credenciales.

8) ¿Tiene que ver “latest” con este error?

No directamente. latest es una etiqueta válida. Solo que provoca otros problemas (deriva, despliegues sorprendentes). Usa etiquetas explícitas o digests y reserva latest solo para desarrollo local.

9) ¿Cuál es la forma más rápida de probar que es un espacio en blanco?

En un shell: printf '%q\n' "$REF". En Python: imprime repr(REF). Si ves \r, \n o un espacio escapado al final, has encontrado al culpable.

Conclusión: siguientes pasos que realmente previenen repeticiones

“invalid reference format” es una queja del analizador, no una sentencia filosófica. Trátala como tratarías una URL mal formada: aisla la cadena exacta, revela caracteres ocultos, valida entradas y deja de permitir que texto libre modele el comportamiento en producción.

Pasos prácticos que implementaría esta semana:

  1. Añade un paso preflight en cada pipeline de CI que imprima la referencia de imagen calculada con escape y falle si contiene caracteres ilegales o está vacía.
  2. Estandariza la generación de etiquetas en un helper compartido, con sanitización, límites de longitud y minúsculas consistentes.
  3. Mueve metadatos a labels OCI en lugar de inflar etiquetas que terminan tropezando con casos límite.
  4. Haz visible el renderizado de Compose: guarda la salida de docker compose config como artefacto en cada job de despliegue.

La solución rara vez es compleja. La disciplina sí lo es. La buena noticia: una vez que dejas de tratar las referencias de imagen como cadenas casuales, este robahoras en particular desaparece de tu vida. Y recuperas unas cuantas horas que Docker te estaba robando en secreto.

← Anterior
Desconexiones IKEv2 en Windows: errores comunes y soluciones fiables
Siguiente →
MySQL vs PostgreSQL: simulacros de PITR—prueba la restauración antes de necesitarla

Deja un comentario