{"record":{"id":"6e58fbccad9a6371","repo":"GoogleContainerTools/skaffold","slug":"bucket-name-is-empty","errorCode":null,"errorMessage":"bucket name is empty","messagePattern":"bucket name is empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/skaffold/gcs/client/native.go","lineNumber":142,"sourceCode":"\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn bucket.UploadObject(ctx, dstObj, f)\n}\n\nfunc (n *Native) parseGCSURI(uri string) (uriInfo, error) {\n\tvar gcsobj uriInfo\n\tu, err := url.Parse(uri)\n\tif err != nil {\n\t\treturn uriInfo{}, fmt.Errorf(\"cannot parse URI %q: %w\", uri, err)\n\t}\n\tif u.Scheme != \"gs\" {\n\t\treturn uriInfo{}, fmt.Errorf(\"URI scheme is %q, must be 'gs'\", u.Scheme)\n\t}\n\tif u.Host == \"\" {\n\t\treturn uriInfo{}, errors.New(\"bucket name is empty\")\n\t}\n\tgcsobj.Bucket = u.Host\n\t// If we do this with the url package it will scape the `?` character, breaking the glob.\n\tgcsobj.ObjPath = strings.TrimLeft(strings.ReplaceAll(uri, \"gs://\"+u.Host, \"\"), \"/\")\n\n\treturn gcsobj, nil\n}\n\nfunc (n *Native) filesToDownload(ctx context.Context, bucket bucketHandler, urinfo uriInfo) (map[string]string, error) {\n\turiToLocalPath := map[string]string{}\n\n\t// The exact match is with the original glob expression. This could be:\n\t// 1. a/b/c -> It will return the file `c` under a/b/ if it exists\n\t// 2. a/b/c* -> It will return any file under a/b/ that starts with c, e.g, c1, c-other, etc\n\t// 3. a/b/c** -> It will return any file that starts with 'c', and files inside any folder starting with 'c'. It is doing the recursion already\n\texactMatches, err := bucket.ListObjects(ctx, &storage.Query{MatchGlob: urinfo.ObjPath})\n\tif err != nil {\n\t\treturn nil, err","sourceCodeStart":124,"sourceCodeEnd":160,"githubUrl":"https://github.com/GoogleContainerTools/skaffold/blob/a1189de023efc32d4b8e11f395acc678aa555011/pkg/skaffold/gcs/client/native.go#L124-L160","documentation":"parseGCSURI parses a gs:// URI into bucket and object components. After url.Parse, it requires the scheme to be 'gs' and the URI host component (the bucket) to be non-empty; an empty host means no bucket name was supplied, so it throws 'bucket name is empty'.","triggerScenarios":"Calling DownloadRecursive or UploadFile with a URI like 'gs://' or 'gs:///path/only' where the authority/bucket part is missing; also with 'gs:///bucket-looks-like-path' forms.","commonSituations":"Config value for a GCS source or cache left as 'gs://' with no bucket; environment variable for bucket name empty so the URI concatenates to 'gs://' + '' + '/obj'; typo dropping the bucket from the path.","solutions":["Check the configured GCS URI and add the bucket name: 'gs://my-bucket/path'","Verify the env/config variable holding the bucket name is non-empty before building the URI","If using a path-style value like 'gs://bucket/obj', confirm no leading slash swallowed the bucket ('gs:///obj' is invalid)"],"exampleFix":"// before\nuri := fmt.Sprintf(\"gs://%s/%s\", os.Getenv(\"BUCKET\"), object) // BUCKET empty -> gs:///obj\n// after\nbucket := os.Getenv(\"BUCKET\")\nif bucket == \"\" { log.Fatal(\"BUCKET env var must be set\") }\nuri := fmt.Sprintf(\"gs://%s/%s\", bucket, object)","handlingStrategy":"validation","validationCode":"u, err := url.Parse(cfg.GCSURI)\nif err != nil || u.Scheme != \"gs\" || u.Host == \"\" {\n    return fmt.Errorf(\"GCS URI %q must include a bucket, e.g. gs://bucket/path\", cfg.GCSURI)\n}","typeGuard":"func hasGCSBucket(uri string) bool {\n    u, err := url.Parse(uri)\n    return err == nil && u.Scheme == \"gs\" && u.Host != \"\"\n}","tryCatchPattern":null,"preventionTips":["Never concatenate bucket names from possibly-empty env vars without a check","Validate gs:// URIs at config load time with a regex like ^gs://[^/]+/.*$","Prefer named config fields (bucket, object) over raw URI strings where possible"],"tags":["gcs","uri-parsing","google-cloud","config"],"backgroundTag":"invalid-uri","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"}