{"record":{"id":"56af2c14f120aa9a","repo":"GoogleContainerTools/skaffold","slug":"cannot-call-run-with-empty-container-config","errorCode":null,"errorMessage":"cannot call Run with empty container config","messagePattern":"cannot call Run with empty container config","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/skaffold/docker/image.go","lineNumber":231,"sourceCode":"// Delete stops, removes, and prunes a running container\nfunc (l *localDaemon) Delete(ctx context.Context, out io.Writer, id string) error {\n\tif _, err := l.apiClient.ContainerStop(ctx, id, client.ContainerStopOptions{}); err != nil {\n\t\tlog.Entry(ctx).Debugf(\"unable to stop running container: %s\", err.Error())\n\t}\n\tif _, err := l.apiClient.ContainerRemove(ctx, id, client.ContainerRemoveOptions{}); err != nil {\n\t\tlog.Entry(ctx).Warnf(\"unable to remove container: %s\", err.Error())\n\t}\n\t_, err := l.apiClient.ContainerPrune(ctx, client.ContainerPruneOptions{})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"pruning removed container: %w\", err)\n\t}\n\treturn nil\n}\n\n// Run creates a container from a given image reference, and returns a wait channel and the container ID.\nfunc (l *localDaemon) Run(ctx context.Context, out io.Writer, opts ContainerCreateOpts) (<-chan container.WaitResponse, <-chan error, string, error) {\n\tif opts.ContainerConfig == nil {\n\t\treturn nil, nil, \"\", fmt.Errorf(\"cannot call Run with empty container config\")\n\t}\n\tc, err := l.apiClient.ContainerCreate(ctx, client.ContainerCreateOptions{\n\t\tConfig: opts.ContainerConfig,\n\t\tHostConfig: &container.HostConfig{\n\t\t\tNetworkMode:  container.NetworkMode(opts.Network),\n\t\t\tVolumesFrom:  opts.VolumesFrom,\n\t\t\tPortBindings: opts.Bindings,\n\t\t\tMounts:       opts.Mounts,\n\t\t},\n\t\tName: opts.Name,\n\t})\n\tif err != nil {\n\t\treturn nil, nil, \"\", err\n\t}\n\tif _, err := l.apiClient.ContainerStart(ctx, c.ID, client.ContainerStartOptions{}); err != nil {\n\t\treturn nil, nil, \"\", err\n\t}\n\tif opts.Wait {","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/GoogleContainerTools/skaffold/blob/a1189de023efc32d4b8e11f395acc678aa555011/pkg/skaffold/docker/image.go#L213-L249","documentation":"localDaemon.Run creates a container from an image reference and returns wait channels plus the container ID. It rejects the call up front with this error when opts.ContainerConfig is nil, because a container cannot be created without a config. It is a defensive programming-contract error, not a daemon failure.","triggerScenarios":"Calling Run(ctx, out, ContainerCreateOpts{...}) with the zero value or with ContainerConfig left unset — e.g. constructing ContainerCreateOpts with only Image/Network/VolumesFrom and forgetting &container.Config{Image: ...}.","commonSituations":"Plugin/custom-builder code calling the LocalDaemon.Run API after partially populating ContainerCreateOpts; refactors that changed ContainerConfig from a value to a pointer and left callers passing nil; dynamic config assembly where an error path skipped config initialization.","solutions":["Set opts.ContainerConfig to a non-nil *container.Config that includes at least the Image field before calling Run.","If options are built conditionally, ensure the default/config-loaded path always assigns ContainerConfig.","If you only wanted an image (not a container), use the image API (e.g. Tag/Push or ImageID) instead of Run.","Update callers broken by an API change so they pass the required config struct."],"exampleFix":"// before\ncw, ch, id, err := daemon.Run(ctx, out, docker.ContainerCreateOpts{Image: \"alpine\"})\n\n// after\ncw, ch, id, err := daemon.Run(ctx, out, docker.ContainerCreateOpts{\n    ContainerConfig: &container.Config{Image: \"alpine\"},\n})","handlingStrategy":"validation","validationCode":"// Go: guard before calling Run\nif opts.ContainerConfig == nil || opts.ContainerConfig.Image == \"\" {\n    return fmt.Errorf(\"ContainerConfig (with Image) must be set before Run\")\n}","typeGuard":"// Go: narrowing helper\nfunc hasContainerConfig(opts docker.ContainerCreateOpts) (*container.Config, bool) {\n    if opts.ContainerConfig == nil || opts.ContainerConfig.Image == \"\" {\n        return nil, false\n    }\n    return opts.ContainerConfig, true\n}","tryCatchPattern":"cw, ch, id, err := daemon.Run(ctx, out, opts)\nif err != nil {\n    if strings.Contains(err.Error(), \"empty container config\") {\n        return fmt.Errorf(\"programmer error: initialize ContainerCreateOpts.ContainerConfig (&container.Config{Image: ...}) before Run\")\n    }\n    return err\n}","preventionTips":["Always construct ContainerCreateOpts via a constructor that sets ContainerConfig with Image.","If creating containers from an image reference, assert cfg.Image != nil first.","Add a unit test asserting Run is never called with zero-value opts in your plugin code.","Watch for API changes that turn value fields into pointers and update callers."],"tags":["docker","container","api-misuse","validation"],"backgroundTag":"missing-required-config","analyzedSha":"a1189de023efc32d4b8e11f395acc678aa555011","analyzedAt":"2026-09-05T12:09:27.064Z","contentChangedAt":"2026-09-05T12:09:27.064Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}