Summary
astro remote deploy builds its remote Docker push tag as:
remoteImage := fmt.Sprintf("%s:%s", registryEndpoint, imageTag)
(cloud/deploy/deploy.go, around lines 1071-1084), where imageTag is "deploy-" + <UTC timestamp> and registryEndpoint comes straight from the remote.client_registry config value.
If a user sets remote.client_registry to a value that itself already contains a :tag -- for example by copy-pasting a full tagged image reference out of a Dockerfile FROM line -- the CLI blindly appends its own :deploy-<timestamp> on top of it, producing a Docker reference with two tags, e.g.:
myregistry.example.com:7990/repository/path/my-image:1.0:deploy-2026-07-22T16:23
Docker then rejects this at build/push time with a raw invalid reference format error, which gives the user no indication that the problem is their remote.client_registry config value rather than something wrong with the build itself. This happened live to a customer mid-PoV and took real time to root-cause because the CLI-level error surfaced as an opaque Docker error.
Root cause
ValidateRegistryEndpoint in config/validators.go (currently lines ~29-51) only checks that the value:
- is non-empty
- contains a
/
- has no spaces
- doesn't start or end with
/
It never rejects an embedded :tag component on the repository path, so a value like myregistry.example.com:7990/repository/path/my-image:1.0 passes validation cleanly and only blows up later, inside Docker, when the CLI's own tag gets appended.
Vulnerable call sites
This validator is the single shared code path for every way a user can set remote.client_registry, so both of these are affected:
astro dev init --remote-execution-enabled -- via getRegistryEndpoint() in cmd/airflow.go, whether the value comes from the --remote-image-repository flag or the interactive prompt.
astro config set remote.client_registry <value> -- in cmd/config.go (~line 112), via cfg.Validate().
There is no separate astro remote init command; both paths funnel into the same ValidateRegistryEndpoint registered against remote.client_registry in config/validators.go (line ~17).
Proposed fix
Extend ValidateRegistryEndpoint to reject any value where the repository-path portion (everything after the first /) contains a :. This correctly still allows a :port on the registry host itself (before the first /), e.g. myregistry.example.com:7990/repository/path remains valid, while rejecting myregistry.example.com:7990/repository/path/my-image:1.0.
Suggested error message:
registry endpoint must not include an image tag -- got %q, use 'registry[:port]/repository/path' without a trailing ':tag' (the CLI appends its own deploy tag automatically)
I have a fix + unit tests ready and will open a PR referencing this issue.
🤖 Filed with the help of Claude Sonnet 5 (Claude Code)
Summary
astro remote deploybuilds its remote Docker push tag as:(
cloud/deploy/deploy.go, around lines 1071-1084), whereimageTagis"deploy-" + <UTC timestamp>andregistryEndpointcomes straight from theremote.client_registryconfig value.If a user sets
remote.client_registryto a value that itself already contains a:tag-- for example by copy-pasting a full tagged image reference out of a DockerfileFROMline -- the CLI blindly appends its own:deploy-<timestamp>on top of it, producing a Docker reference with two tags, e.g.:Docker then rejects this at build/push time with a raw
invalid reference formaterror, which gives the user no indication that the problem is theirremote.client_registryconfig value rather than something wrong with the build itself. This happened live to a customer mid-PoV and took real time to root-cause because the CLI-level error surfaced as an opaque Docker error.Root cause
ValidateRegistryEndpointinconfig/validators.go(currently lines ~29-51) only checks that the value://It never rejects an embedded
:tagcomponent on the repository path, so a value likemyregistry.example.com:7990/repository/path/my-image:1.0passes validation cleanly and only blows up later, inside Docker, when the CLI's own tag gets appended.Vulnerable call sites
This validator is the single shared code path for every way a user can set
remote.client_registry, so both of these are affected:astro dev init --remote-execution-enabled-- viagetRegistryEndpoint()incmd/airflow.go, whether the value comes from the--remote-image-repositoryflag or the interactive prompt.astro config set remote.client_registry <value>-- incmd/config.go(~line 112), viacfg.Validate().There is no separate
astro remote initcommand; both paths funnel into the sameValidateRegistryEndpointregistered againstremote.client_registryinconfig/validators.go(line ~17).Proposed fix
Extend
ValidateRegistryEndpointto reject any value where the repository-path portion (everything after the first/) contains a:. This correctly still allows a:porton the registry host itself (before the first/), e.g.myregistry.example.com:7990/repository/pathremains valid, while rejectingmyregistry.example.com:7990/repository/path/my-image:1.0.Suggested error message:
I have a fix + unit tests ready and will open a PR referencing this issue.
🤖 Filed with the help of Claude Sonnet 5 (Claude Code)