benbjohnson/litestream · error

oss: get object %s: %w

Error message

oss: get object %s: %w

What it means

GetObject on the OSS bucket failed while opening an LTX file for read. The client first checks isNotExists(err) and translates a missing object into os.ErrNotExist; any other error (network, auth, permissions, invalid key) is wrapped with the object key for context.

Source

Thrown at oss/replica_client.go:222

		Bucket: oss.Ptr(c.Bucket),
		Key:    oss.Ptr(key),
	}

	// Set range header if offset is specified
	if size > 0 {
		request.RangeBehavior = oss.Ptr("standard")
		request.Range = oss.Ptr(fmt.Sprintf("bytes=%d-%d", offset, offset+size-1))
	} else if offset > 0 {
		request.RangeBehavior = oss.Ptr("standard")
		request.Range = oss.Ptr(fmt.Sprintf("bytes=%d-", offset))
	}

	result, err := c.client.GetObject(ctx, request)
	if err != nil {
		if isNotExists(err) {
			return nil, os.ErrNotExist
		}
		return nil, fmt.Errorf("oss: get object %s: %w", key, err)
	}

	internal.OperationTotalCounterVec.WithLabelValues(ReplicaClientType, "GET").Inc()

	return result.Body, nil
}

// WriteLTXFile writes an LTX file to the replica.
// Extracts timestamp from LTX header and stores it in OSS metadata to preserve original creation time.
// Uses multipart upload for large files via the uploader.
func (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, r io.Reader) (*ltx.FileInfo, error) {
	if err := c.Init(ctx); err != nil {
		return nil, err
	}

	// Use TeeReader to peek at LTX header while preserving data for upload
	var buf bytes.Buffer
	teeReader := io.TeeReader(r, &buf)

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Inspect the wrapped underlying error for the OSS error code (AccessDenied, NoSuchBucket, RequestError) and fix accordingly.
  2. Verify credentials (AccessKeyID/Secret) are valid and the RAM policy grants oss:GetObject on the bucket/path.
  3. Confirm region/endpoint match the bucket's actual region (default is cn-hangzhou if Region is unset).
  4. If the object is genuinely gone, restore from an older available LTX file or re-run 'litestream restore' after checking 'litestream ltx -level all'.
  5. Retry on transient network errors; Litestream sync will retry on the next sync interval.

Example fix

// before
r, err := client.OpenLTXFile(ctx, pos)
if err != nil { return err }
// after
r, err := client.OpenLTXFile(ctx, pos)
if errors.Is(err, os.ErrNotExist) {
    return nil // file not replicated yet; not fatal
} else if err != nil {
    return fmt.Errorf("read ltx: %w", err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if _, err := client.HeadBucket(ctx); err != nil { /* credentials/region wrong before any read */ }

Try / catch

r, err := client.OpenLTXFile(ctx, key)
if errors.Is(err, os.ErrNotExist) {
    return nil // expected: object not yet replicated
}
if err != nil {
    return fmt.Errorf("open ltx from oss: %w", err) // inspect wrapped OSS code
}

Prevention

When it happens

Trigger: Calling OpenLTXFile for a key that exists but is inaccessible: expired/invalid credentials, wrong region endpoint, object deleted between listing and read, or transient network failure during restore/sync.

Common situations: Restoring a database whose LTX files were removed by a lifecycle rule; IAM/RAM policy lacking oss:GetObject; bucket in a different region than the configured endpoint; TLS/proxy interference in corporate networks.

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/70a2b6b620804e77. Report an issue: GitHub.