{"record":{"id":"d96a5830d18610f3","repo":"benbjohnson/litestream","slug":"gs-cannot-delete-ltx-file-q-w","errorCode":null,"errorMessage":"gs: cannot delete ltx file %q: %w","messagePattern":"gs: cannot delete ltx file %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"gs/replica_client.go","lineNumber":231,"sourceCode":"\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"GET\").Inc()\n\tinternal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, \"GET\").Add(float64(r.Attrs.Size))\n\n\treturn r, nil\n}\n\n// DeleteLTXFiles deletes a set of LTX files.\nfunc (c *ReplicaClient) DeleteLTXFiles(ctx context.Context, a []*ltx.FileInfo) error {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn err\n\t}\n\n\tfor _, info := range a {\n\t\tkey := litestream.LTXFilePath(c.Path, info.Level, info.MinTXID, info.MaxTXID)\n\n\t\tc.logger.Debug(\"deleting ltx file\", \"level\", info.Level, \"minTXID\", info.MinTXID, \"maxTXID\", info.MaxTXID, \"key\", key)\n\n\t\tif err := c.bkt.Object(key).Delete(ctx); err != nil && !isNotExists(err) {\n\t\t\treturn fmt.Errorf(\"gs: cannot delete ltx file %q: %w\", key, err)\n\t\t}\n\t\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"DELETE\").Inc()\n\t}\n\n\treturn nil\n}\n\ntype ltxFileIterator struct {\n\tit     *storage.ObjectIterator\n\tclient *ReplicaClient\n\tlevel  int\n\tinfo   *ltx.FileInfo\n\terr    error\n}\n\nfunc newLTXFileIterator(it *storage.ObjectIterator, client *ReplicaClient, level int) *ltxFileIterator {\n\treturn &ltxFileIterator{\n\t\tit:     it,","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/gs/replica_client.go#L213-L249","documentation":"DeleteLTXFiles() failed to delete a specific LTX object in GCS. Unlike DeleteAll, this loop only tolerates NotExists errors explicitly; any other delete error (permissions, retention lock, transient API failure) aborts the deletion batch with the object key named.","triggerScenarios":"Calling DeleteLTXFiles() during retention/compaction cleanup when the service account lacks storage.objects.delete, a GCS retention policy holds the object, or a transient API error occurs mid-batch.","commonSituations":"Read-only credentials used for replication (write-only or viewer role); bucket with object versioning/retention enabled; GCS throttling during large retention sweeps.","solutions":["Grant roles/storage.objectAdmin to the litestream service account on the bucket","Check the wrapped error's status code; 403 => permissions, 429/5xx => retry","Verify no bucket retention policy or hold prevents object deletion","Re-run retention — remaining deletions are retried on the next sweep since it aborts at first failure","Check connectivity/proxy settings if errors are transport-related"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := rc.DeleteLTXFiles(ctx, a); err != nil {\n    if isRetryable(err) { // 429/5xx\n        time.Sleep(backoff)\n        return rc.DeleteLTXFiles(ctx, a)\n    }\n    return err\n}","preventionTips":["Grant delete permissions (objectAdmin) to the replica service account","Check retention policies before enabling retention cleanup","Keep litestream updated so NotExists handling stays correct"],"tags":["gcs","cloud-storage","retention","delete"],"backgroundTag":"permission-denied","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"}