{"record":{"id":"71319f042dc999f7","repo":"docker/cli","slug":"empty-string-specified-for-links","errorCode":null,"errorMessage":"empty string specified for links","messagePattern":"empty string specified for links","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"opts/opts.go","lineNumber":374,"sourceCode":"}\n\n// ParseCPUs takes a string ratio and returns an integer value of nano cpus\nfunc ParseCPUs(value string) (int64, error) {\n\tcpu, ok := new(big.Rat).SetString(value)\n\tif !ok {\n\t\treturn 0, fmt.Errorf(\"failed to parse %v as a rational number\", value)\n\t}\n\tnano := cpu.Mul(cpu, big.NewRat(1e9, 1))\n\tif !nano.IsInt() {\n\t\treturn 0, errors.New(\"value is too precise\")\n\t}\n\treturn nano.Num().Int64(), nil\n}\n\n// ParseLink parses and validates the specified string as a link format (name:alias)\nfunc ParseLink(val string) (string, string, error) {\n\tif val == \"\" {\n\t\treturn \"\", \"\", errors.New(\"empty string specified for links\")\n\t}\n\t// We expect two parts, but restrict to three to allow detecting invalid formats.\n\tarr := strings.SplitN(val, \":\", 3)\n\n\t// TODO(thaJeztah): clean up this logic!!\n\tif len(arr) > 2 {\n\t\treturn \"\", \"\", errors.New(\"bad format for links: \" + val)\n\t}\n\t// TODO(thaJeztah): this should trim the \"/\" prefix as well??\n\tif len(arr) == 1 {\n\t\treturn val, val, nil\n\t}\n\t// This is kept because we can actually get a HostConfig with links\n\t// from an already created container and the format is not `foo:bar`\n\t// but `/foo:/c1/bar`\n\tif strings.HasPrefix(arr[0], \"/\") {\n\t\t// TODO(thaJeztah): clean up this logic!!\n\t\t_, alias := path.Split(arr[1])","sourceCodeStart":356,"sourceCodeEnd":392,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/opts/opts.go#L356-L392","documentation":"Thrown by opts.ParseLink when the input string is empty. ParseLink parses Docker's legacy container-link format (\"name:alias\") used by --link; an empty string has no container name to resolve, so it is rejected outright. This guard sits at the very top of the function before any splitting occurs.","triggerScenarios":"Calling opts.ParseLink(\"\"), passing --link \"\" to docker run/create, or constructing a HostConfig.Links slice that contains an empty element.","commonSituations":"Shell scripting docker run with an unset/empty link variable, e.g. LINK=\"\"; docker run --link \"$LINK:alias\". Also happens when programmatically building the Links slice from filtered or missing data.","solutions":["Omit the --link flag entirely when no link is needed.","Ensure the link variable is non-empty before passing it: guard with a length/emptiness check.","If building Links programmatically, filter out empty strings from the slice before submission."],"exampleFix":"// before\nname, alias, err := opts.ParseLink(\"\")\n// after\nif l == \"\" {\n    return errors.New(\"link must not be empty\")\n}\nname, alias, err := opts.ParseLink(l)","handlingStrategy":"validation","validationCode":"if strings.TrimSpace(link) == \"\" {\n    return fmt.Errorf(\"link must not be empty\")\n}\nname, alias, err := opts.ParseLink(link)","typeGuard":null,"tryCatchPattern":"name, alias, err := opts.ParseLink(link)\nif err != nil {\n    // log err; skip this link or abort\n    return err\n}","preventionTips":["Filter empty strings out of any Links slice before submission.","Guard shell variables with [ -n \"$VAR\" ] before passing to --link."],"tags":["links","legacy","validation","opts"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}