{"record":{"id":"a2c19848397f8f2b","repo":"apache/beam","slug":"object-s-must-have-bucket","errorCode":null,"errorMessage":"object %s must have bucket","messagePattern":"object (.+?) must have bucket","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"sdks/go/pkg/beam/util/gcsx/gcs.go","lineNumber":145,"sourceCode":"func MakeObject(bucket, path string) string {\n\tif bucket == \"\" {\n\t\tpanic(\"bucket must be non-empty\")\n\t}\n\treturn fmt.Sprintf(\"gs://%v/%v\", bucket, path)\n}\n\n// ParseObject deconstructs a GCS object name into (bucket, name).\nfunc ParseObject(object string) (bucket, path string, err error) {\n\tparsed, err := url.Parse(object)\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\n\tif parsed.Scheme != \"gs\" {\n\t\treturn \"\", \"\", errors.Errorf(\"object %s must have 'gs' scheme\", object)\n\t}\n\tif parsed.Host == \"\" {\n\t\treturn \"\", \"\", errors.Errorf(\"object %s must have bucket\", object)\n\t}\n\tif parsed.Path == \"\" {\n\t\treturn parsed.Host, \"\", nil\n\t}\n\n\t// remove leading \"/\" in URL path\n\treturn parsed.Host, parsed.Path[1:], nil\n}\n\n// Join joins a GCS path with an element. Preserves\n// the gs:// prefix.\nfunc Join(object string, elms ...string) string {\n\tbucket, prefix, err := ParseObject(object)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn MakeObject(bucket, path.Join(prefix, path.Join(elms...)))\n}","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/go/pkg/beam/util/gcsx/gcs.go#L127-L163","documentation":"gcsx.ParseObject requires the parsed gs:// URL to have a bucket host. This error is returned when the scheme is 'gs' but the URL has no host component — e.g. the bare string 'gs://' or 'gs:///path' with no bucket name.","triggerScenarios":"Calling gcsx.ParseObject (directly or via List, OpenRead, staging/retrieval servers, ReadProxyManifest) with 'gs://' or 'gs:///object' where parsed.Host is empty.","commonSituations":"Configuration with a truncated GCS URI, template variables left unexpanded (e.g. 'gs://%s/' with an empty variable), or staging paths built from an empty bucket-name config value.","solutions":["Include the bucket in the URI: 'gs://bucket-name/object-path'","Check the config/env providing the bucket name isn't empty","Validate with a quick parse before calling: u, _ := url.Parse(obj); ensure u.Scheme==\"gs\" && u.Host!=\"\""],"exampleFix":"// before\ngcsx.OpenRead(ctx, client, \"gs://\" + bucket + \"/obj\") // bucket == \"\"\n// after\nif bucket == \"\" { return errors.New(\"bucket not configured\") }\ngcsx.OpenRead(ctx, client, \"gs://\" + bucket + \"/obj\")","handlingStrategy":"validation","validationCode":"u, err := url.Parse(obj)\nif err == nil && u.Scheme == \"gs\" && u.Host == \"\" {\n\treturn fmt.Errorf(\"gs URI %q missing bucket\", obj)\n}","typeGuard":"func hasGCSBucket(s string) bool {\n\tu, err := url.Parse(s)\n\treturn err == nil && u.Scheme == \"gs\" && u.Host != \"\"\n}","tryCatchPattern":"if err != nil {\n\tif strings.Contains(err.Error(), \"must have bucket\") {\n\t\treturn fmt.Errorf(\"GCS URI %q missing bucket name\", obj)\n\t}\n\treturn err\n}","preventionTips":["Assert bucket name config is non-empty before building URIs","Expand template variables before composing gs:// URIs","Add a startup config check for all GCS paths"],"tags":["gcs","url","validation","apache-beam"],"backgroundTag":"missing-required-argument","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}