{"record":{"id":"3e337909c01153e2","repo":"docker/cli","slug":"invalid-type-t-for-external","errorCode":null,"errorMessage":"invalid type %T for external","messagePattern":"invalid type %T for external","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cli/compose/loader/loader.go","lineNumber":703,"sourceCode":"var transformMapStringString TransformerFunc = func(data any) (any, error) {\n\tswitch value := data.(type) {\n\tcase map[string]any:\n\t\treturn toMapStringString(value, false), nil\n\tcase map[string]string:\n\t\treturn value, nil\n\tdefault:\n\t\treturn data, fmt.Errorf(\"invalid type %T for map[string]string\", value)\n\t}\n}\n\nvar transformExternal TransformerFunc = func(data any) (any, error) {\n\tswitch value := data.(type) {\n\tcase bool:\n\t\treturn map[string]any{\"external\": value}, nil\n\tcase map[string]any:\n\t\treturn map[string]any{\"external\": true, \"name\": value[\"name\"]}, nil\n\tdefault:\n\t\treturn data, fmt.Errorf(\"invalid type %T for external\", value)\n\t}\n}\n\nvar transformServicePort TransformerFunc = func(data any) (any, error) {\n\tswitch entries := data.(type) {\n\tcase []any:\n\t\t// We process the list instead of individual items here.\n\t\t// The reason is that one entry might be mapped to multiple ServicePortConfig.\n\t\t// Therefore we take an input of a list and return an output of a list.\n\t\tports := []any{}\n\t\tfor _, entry := range entries {\n\t\t\tswitch value := entry.(type) {\n\t\t\tcase int:\n\t\t\t\tv, err := toServicePortConfigs(strconv.Itoa(value))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn data, err\n\t\t\t\t}\n\t\t\t\tports = append(ports, v...)","sourceCodeStart":685,"sourceCodeEnd":721,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/cli/compose/loader/loader.go#L685-L721","documentation":"Thrown by transformExternal, the transformer for the `external` field on networks/volumes/secrets/configs. It accepts only a boolean (`external: true`) or a mapping (`external: { name: x }`); any other type (string, list, number) is rejected.","triggerScenarios":"A resource sets `external: \"true\"` (string), `external: [true]`, or `external: yes-maybe`. The transformer runs during Transform() of the resource block.","commonSituations":"Quoting `true`, using a placeholder/template substitution that yields a non-bool, or env interpolation that produces a string.","solutions":["Use a bare boolean: `external: true`.","Or use the map form: `external: { name: my-resource }`."],"exampleFix":"# before\nvolumes:\n  data:\n    external: \"true\"\n# after\nvolumes:\n  data:\n    external: true","handlingStrategy":"type-guard","validationCode":"func validateExternal(v any) error {\n    switch v.(type) {\n    case bool, map[string]any:\n        return nil\n    default:\n        return fmt.Errorf(\"external must be bool or map, got %T\", v)\n    }\n}","typeGuard":"func isValidExternal(v any) bool {\n    switch v.(type) {\n    case bool:\n        return true\n    case map[string]any:\n        return true\n    }\n    return false\n}","tryCatchPattern":null,"preventionTips":["Keep `external` as a bare boolean or a `{ name: ... }` map.","Beware env interpolation that turns `external` into a string.","Lint configs before deploy."],"tags":["compose","external","type-mismatch","volume","network"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}