{"record":{"id":"f0eb3453b60bf540","repo":"benbjohnson/litestream","slug":"bucket-required-for-gs-replica-url","errorCode":null,"errorMessage":"bucket required for gs replica URL","messagePattern":"bucket required for gs replica URL","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gs/replica_client.go","lineNumber":63,"sourceCode":"\tPath   string\n}\n\n// NewReplicaClient returns a new instance of ReplicaClient.\nfunc NewReplicaClient() *ReplicaClient {\n\treturn &ReplicaClient{\n\t\tlogger: slog.Default().WithGroup(ReplicaClientType),\n\t}\n}\n\nfunc (c *ReplicaClient) SetLogger(logger *slog.Logger) {\n\tc.logger = logger.WithGroup(ReplicaClientType)\n}\n\n// NewReplicaClientFromURL creates a new ReplicaClient from URL components.\n// This is used by the replica client factory registration.\nfunc NewReplicaClientFromURL(scheme, host, urlPath string, query url.Values, userinfo *url.Userinfo) (litestream.ReplicaClient, error) {\n\tif host == \"\" {\n\t\treturn nil, fmt.Errorf(\"bucket required for gs replica URL\")\n\t}\n\n\tclient := NewReplicaClient()\n\tclient.Bucket = host\n\tclient.Path = urlPath\n\treturn client, nil\n}\n\n// Type returns \"gs\" as the client type.\nfunc (c *ReplicaClient) Type() string {\n\treturn ReplicaClientType\n}\n\n// Init initializes the connection to GS. No-op if already initialized.\nfunc (c *ReplicaClient) Init(ctx context.Context) (err error) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n","sourceCodeStart":45,"sourceCodeEnd":81,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/gs/replica_client.go#L45-L81","documentation":"The GCS replica client factory requires the URL host to name the target bucket. NewReplicaClientFromURL returns this error when a gs:// replica URL has no host component, since the client would otherwise have no bucket to operate on.","triggerScenarios":"Configuring a replica with URL gs:///some/path (empty host), gs:// with nothing after the scheme, or a templated config where the bucket variable expanded to empty, then instantiating via the URL factory.","commonSituations":"Missing bucket in the litestream.yml replica URL; ${GCS_BUCKET} unset at process start; copying an S3-style path-only URL into a gs:// scheme.","solutions":["Include the bucket as the URL host: gs://my-bucket/path/to/replica.","Ensure any env var used for the bucket name is set in the environment at litestream startup.","Validate replica URLs at config-load time: for the gs scheme, require a non-empty host.","Confirm credentials/service account separately; this error is purely about URL shape."],"exampleFix":"// before (litestream.yml)\nreplicas:\n  - url: gs:///backups/db\n// after\nreplicas:\n  - url: gs://my-bucket/backups/db","handlingStrategy":"validation","validationCode":"u, err := url.Parse(replicaURL)\nif err != nil || u.Scheme != \"gs\" { return fmt.Errorf(\"not a gs replica URL\") }\nif u.Host == \"\" { return fmt.Errorf(\"bucket required for gs replica URL\") }","typeGuard":"func validGSReplicaURL(u *url.URL) bool { return u != nil && u.Scheme == \"gs\" && u.Host != \"\" }","tryCatchPattern":"if _, err := NewReplicaClientFromURL(\"gs\", u.Host, u.Path, u.Query(), u.User); err != nil {\n    return fmt.Errorf(\"invalid gs replica URL %q: %w\", replicaURL, err)\n}","preventionTips":["Always format gs URLs as gs://bucket/prefix.","Verify bucket-name env vars are set before litestream starts.","Validate all replica URLs at config load time."],"tags":["config","url","gcs","validation"],"backgroundTag":"missing-required-argument","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}