benbjohnson/litestream · error

oss: upload to %s: %w

Error message

oss: upload to %s: %w

What it means

The OSS uploader's UploadFrom call failed while streaming an LTX object to the bucket. This wraps any SDK/network error from the PUT: auth failures, bucket/region mismatch, size limits, or mid-stream connection drops.

Source

Thrown at oss/replica_client.go:267

	// Combine buffered data with rest of reader
	rc := internal.NewReadCounter(io.MultiReader(&buf, r))

	filename := ltx.FormatFilename(minTXID, maxTXID)
	key := c.ltxPath(level, filename)

	// Store timestamp in OSS metadata for accurate timestamp retrieval
	metadata := map[string]string{
		MetadataKeyTimestamp: timestamp.Format(time.RFC3339Nano),
	}

	// Use uploader for automatic multipart handling (files >5GB)
	result, err := c.uploader.UploadFrom(ctx, &oss.PutObjectRequest{
		Bucket:   oss.Ptr(c.Bucket),
		Key:      oss.Ptr(key),
		Metadata: metadata,
	}, rc)
	if err != nil {
		return nil, fmt.Errorf("oss: upload to %s: %w", key, err)
	}

	// Build file info from the uploaded file
	info := &ltx.FileInfo{
		Level:     level,
		MinTXID:   minTXID,
		MaxTXID:   maxTXID,
		Size:      rc.N(),
		CreatedAt: timestamp,
	}

	internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "PUT").Inc()
	internal.OperationBytesCounterVec.WithLabelValues(ReplicaClientType, "PUT").Add(float64(rc.N()))

	// ETag indicates successful upload
	if result.ETag == nil || *result.ETag == "" {
		return nil, fmt.Errorf("oss: upload failed: no ETag returned")
	}

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Read the wrapped OSS error code: fix AccessDenied by granting oss:PutObject; fix NoSuchBucket by creating the bucket.
  2. Verify region/endpoint matches the bucket (set region explicitly in config instead of relying on the cn-hangzhou default).
  3. Rotate/refresh credentials if the error indicates signature or token expiration (STS tokens expire).
  4. Check network stability/proxy config; Litestream will retry the upload on the next sync.

Example fix

// before
replicas:
  - type: oss
    bucket: my-bucket
    path: ltx
// after
replicas:
  - type: oss
    bucket: my-bucket
    region: oss-cn-beijing
    endpoint: https://oss-cn-beijing.aliyuncs.com
    path: ltx
Defensive patterns

Strategy: retry

Validate before calling

if err := client.Init(ctx); err != nil { return err } // fails fast on bad config/creds
if _, err := client.HeadBucket(ctx); err != nil { return err }

Try / catch

_, err := client.WriteLTXFile(ctx, key, metadata, r, size)
if err != nil {
    var netErr net.Error
    if errors.As(err, &netErr) || isRetryable(err) {
        time.Sleep(backoff); return retry()
    }
    return fmt.Errorf("upload ltx: %w", err)
}

Prevention

When it happens

Trigger: WriteLTXFile uploading a newly created LTX file when credentials expire, the network drops mid-upload, the bucket does not exist in the target region, or the RAM policy lacks oss:PutObject.

Common situations: Stale AccessKey after rotation; bucket deleted or renamed; regional endpoint misconfigured (defaulting to cn-hangzhou when the bucket lives elsewhere); large compaction files hitting transient timeouts on slow links.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/bd0dd3fe406b3709. Report an issue: GitHub.