benbjohnson/litestream · error

oss: get object

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.

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.

Appendix: 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)