You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Container create fails when a feature contributes a mount whose source uses a substitution variable. Observed with the docker-in-docker feature, which declares:
create dind-var-lib-docker-${devcontainerId}: "dind-var-lib-docker-${devcontainerId}" includes invalid characters for a local volume name, only "[a-zA-Z0-9][a-zA-Z0-9_.-]" are allowed.
The literal ${devcontainerId} reaches ContainerCreate because it was never substituted.
Root cause
config/resolve.go:190 — substituteAll runs as part of ResolveBytes. This correctly resolves ${devcontainerId} in the user'sdevcontainer.json mounts.
No substitution runs after the merge. Feature mount sources keep their literal ${devcontainerId} and flow straight into the ContainerCreate payload.
Same risk exists for any other string field a feature/base-metadata layer can contribute that may include substitution variables (containerEnv, remoteEnv, lifecycle hooks).
Suggested fix
Two shapes:
Local fix: in applyMetadataMerge (up.go:897), after MergeMetadata, run a substitution pass over the merged-in fields. Rebuild a SubstitutionContext from cfg.DevcontainerID, cfg.LocalWorkspaceFolder, cfg.ContainerWorkspaceFolder, and the same localEnvResolveBytes saw.
Cleaner: have MergeMetadata accept a SubstitutionContext and substitute as it folds layers in, preserving the invariant that every string in ResolvedConfig is host-substituted.
Leaning toward the second so callers don't have to remember.
Repro
Any devcontainer that depends on the docker-in-docker feature (e.g. dap-workspace-55 in dap-ephraim-sandbox).
Activity
config: substitute variables in feature-contributed metadata (#53)