{"record":{"id":"b8e6e6f820382fbf","repo":"benbjohnson/litestream","slug":"s3-upload-to-s-part-size-must-be-at-least-d-by","errorCode":null,"errorMessage":"s3: upload to %s: part size must be at least %d bytes","messagePattern":"s3: upload to (.+?): part size must be at least (.+?) bytes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"s3/replica_client.go","lineNumber":735,"sourceCode":"// to avoid the multipart upload manager's 5 MiB part buffers (issue #1327).\nfunc (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, r io.Reader) (*ltx.FileInfo, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\tfilename := ltx.FormatFilename(minTXID, maxTXID)\n\tkey := c.Path + \"/\" + fmt.Sprintf(\"%04x/%s\", level, filename)\n\n\tpartSize := int64(manager.DefaultUploadPartSize)\n\tif c.PartSize > 0 {\n\t\tpartSize = c.PartSize\n\t}\n\n\t// The uploader rejects part sizes below the SDK minimum on every upload.\n\t// Fail identically here so the single-put path cannot mask the\n\t// misconfiguration for small objects.\n\tif partSize < manager.MinUploadPartSize {\n\t\treturn nil, fmt.Errorf(\"s3: upload to %s: part size must be at least %d bytes\", key, manager.MinUploadPartSize)\n\t}\n\n\tsize, timestamp, etag, err := c.uploadLTX(ctx, key, r, partSize)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tinternal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Inc()\n\tinternal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, \"PUT\").Add(float64(size))\n\n\t// ETag indicates successful upload\n\tif etag == nil {\n\t\treturn nil, fmt.Errorf(\"s3: upload failed: no ETag returned\")\n\t}\n\n\treturn &ltx.FileInfo{\n\t\tLevel:     level,\n\t\tMinTXID:   minTXID,","sourceCodeStart":717,"sourceCodeEnd":753,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/s3/replica_client.go#L717-L753","documentation":"WriteLTXFile refuses to upload when the configured multipart part size is below the AWS SDK's manager.MinUploadPartSize (5 MiB). The check mirrors the uploader's own validation so the misconfiguration fails identically on the single-put path instead of silently succeeding for small objects. It is a deterministic configuration error, not a transient fault.","triggerScenarios":"Calling WriteLTXFile with a partSize argument smaller than manager.MinUploadPartSize (e.g. a config file sets a custom part size like 1MB, or code passes a hand-computed value below 5*1024*1024).","commonSituations":"Users tune upload part sizes in litestream YAML to reduce memory or costs and pick a value under the S3 5 MiB minimum; copying part-size defaults from older SDK versions where the minimum differed.","solutions":["Set partSize to at least manager.MinUploadPartSize (5*1024*1024 bytes) in the config or call site","Clamp the value programmatically: if partSize < s3manager.MinUploadPartSize { partSize = s3manager.MinUploadPartSize } before calling WriteLTXFile","If the value comes from config, validate it at config-load time so the process fails fast at startup"],"exampleFix":"// before\npartSize := 1024 * 1024 // 1 MiB\nclient.WriteLTXFile(ctx, key, r, level, minTXID, maxTXID, timestamp, partSize)\n\n// after\nconst minPartSize = 5 * 1024 * 1024\nif partSize < minPartSize {\n    partSize = minPartSize\n}\nclient.WriteLTXFile(ctx, key, r, level, minTXID, maxTXID, timestamp, partSize)","handlingStrategy":"validation","validationCode":"if partSize < s3manager.MinUploadPartSize {\n    return fmt.Errorf(\"partSize %d below SDK minimum %d\", partSize, s3manager.MinUploadPartSize)\n}","typeGuard":null,"tryCatchPattern":"var cfgErr *fmt.Errorf // match message\nif err != nil && strings.Contains(err.Error(), \"part size must be at least\") {\n    // fix config and restart; retrying will not help\n}","preventionTips":["Never configure part sizes below 5 MiB for S3 uploads","Validate part-size config values at startup, before the first upload","Copy minimums from manager.MinUploadPartSize instead of hardcoding literals"],"tags":["s3","config","validation","upload"],"backgroundTag":"invalid-config-value","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"}