{"record":{"id":"520fe84a03399f06","repo":"docker/cli","slug":"service-s-w","errorCode":null,"errorMessage":"service %s: %w","messagePattern":"service (.+?): %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/compose/convert/service.go","lineNumber":44,"sourceCode":"\nconst (\n\tdefaultNetwork = \"default\"\n\t// LabelImage is the label used to store image name provided in the compose file\n\tLabelImage = \"com.docker.stack.image\"\n)\n\n// Services from compose-file types to engine API types\nfunc Services(\n\tctx context.Context,\n\tnamespace Namespace,\n\tconfig *composetypes.Config,\n\tapiClient client.APIClient,\n) (map[string]swarm.ServiceSpec, error) {\n\tresult := make(map[string]swarm.ServiceSpec)\n\tfor _, service := range config.Services {\n\t\tsecrets, err := convertServiceSecrets(ctx, apiClient, namespace, service.Secrets, config.Secrets)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"service %s: %w\", service.Name, err)\n\t\t}\n\t\tconfigs, err := convertServiceConfigObjs(ctx, apiClient, namespace, service, config.Configs)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"service %s: %w\", service.Name, err)\n\t\t}\n\n\t\tserviceSpec, err := Service(namespace, service, config.Networks, config.Volumes, secrets, configs)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"service %s: %w\", service.Name, err)\n\t\t}\n\t\tresult[service.Name] = serviceSpec\n\t}\n\n\treturn result, nil\n}\n\n// Service converts a ServiceConfig into a swarm ServiceSpec\nfunc Service(","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli/compose/convert/service.go#L26-L62","documentation":"Wrap error produced in the Services() conversion loop when convertServiceSecrets fails for a service (service.go:42-44). It prefixes the inner error with the service name so you know which service's secret references were invalid. The inner error is typically 'undefined secret %q' (from service.go:255) or a failure from servicecli.ParseSecrets contacting the Swarm API.","triggerScenarios":"Calling convert.Services(...) on a compose Config whose service references a secret not declared in the top-level secrets map, or when the Swarm API client cannot resolve a referenced secret by ID/name.","commonSituations":"A service lists `secrets: [mysecret]` but the top-level `secrets:` block omits it; renamed a secret in one place but not both; the secret object does not exist in Swarm and is not marked external; API connectivity issues during ParseSecrets.","solutions":["Add the missing secret definition under the top-level `secrets:` key in the compose file.","If the secret lives outside compose, declare it with `external: true` so compose does not try to create it.","Check the inner error (unwrapped via errors.Is/As) to distinguish 'undefined secret' from a Swarm API failure and fix accordingly."],"exampleFix":"// before\nservices:\n  web:\n    image: nginx\n    secrets:\n      - tls-cert\n// after\nservices:\n  web:\n    image: nginx\n    secrets:\n      - tls-cert\nsecrets:\n  tls-cert:\n    file: ./certs/tls.pem","handlingStrategy":"try-catch","validationCode":"// Pre-check that every secret a service uses is declared before calling convert.Services.\nfunc validateSecrets(cfg *composetypes.Config) error {\n    for _, svc := range cfg.Services {\n        for _, s := range svc.Secrets {\n            if _, ok := cfg.Secrets[s.Source]; !ok {\n                return fmt.Errorf(\"service %s: undefined secret %q\", svc.Name, s.Source)\n            }\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"result, err := convert.Services(ctx, namespace, cfg, apiClient)\nif err != nil {\n    var inner error\n    if errors.As(err, &inner) {\n        // log svcName + inner; surface a user-friendly message\n    }\n    return err\n}","preventionTips":["Declare every secret referenced by a service in the top-level secrets block.","Run a linter (docker compose config) before stack deploy to catch undefined secrets early.","Use external: true for Swarm-managed secrets so compose does not try to create them."],"tags":["compose","convert","secrets","swarm","service"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}