JuliusBrussee/caveman · error
objectstore: get
Error message
objectstore: get %q: %w
What it means
minioStore.Get wraps MinIO GetObject errors with 'objectstore: get %q: %w', naming the missing or unreadable key. It throws when the object stat/fetch fails — most commonly because the key does not exist — as well as for auth, network, and permission failures.
Solutions
- Check the wrapped code: treat NoSuchKey as an expected miss and return a not-found result instead of a 500.
- Verify the exact key (prefix, encoding, trailing slashes) matches what Put wrote; log both sides.
- Confirm credentials/IAM allow GetObject on the bucket.
- Check connectivity to the endpoint and retry transient network errors with backoff.
Example fix
// before
body, err := store.Get(ctx, key)
if err != nil { return err } // 500 on every missing object
// after
body, err := store.Get(ctx, key)
if err != nil {
var resp minio.ErrorResponse
if errors.As(err, &resp) && resp.Code == "NoSuchKey" {
return nil, ErrObjectNotFound
}
return err
} Defensive patterns
Strategy: try-catch
Try / catch
body, err := store.Get(ctx, key)
if err != nil {
var resp minio.ErrorResponse
if errors.As(err, &resp) && resp.Code == "NoSuchKey" {
return nil, ErrObjectNotFound // expected miss, not a 500
}
return fmt.Errorf("get failed: %w", err) // retryable for transient codes
} Prevention
- Map NoSuchKey to a domain not-found error instead of a generic failure.
- Keep key construction (prefix/encoding) in one shared helper used by Put and Get.
- Grant GetObject in IAM alongside Put/Delete.
- Retry only transient errors; do not retry NoSuchKey.
When it happens
Trigger: Calling Store.Get for a key that was never written or was deleted/purged (Probe cleanup, version expiry), or when the GetObject call fails on network/auth/permission; the error is wrapped before reading the body.
Common situations: Reading an object after its TTL/purge cycle; key constructed with a different prefix or encoding than the writer used; credentials lacking s3:GetObject; MinIO endpoint unreachable after restart.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- objectstore: object not found
- objectstore: production read/write/delete probe failed
- objectstore: put
- binary download failed
- binary download failed: HTTP
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/0f7c3c70558eee23.
Report an issue: GitHub.
Appendix: source
Thrown at shared/platform/objectstore/objectstore.go:286
return nil
}
func (m *minioStore) Put(ctx context.Context, key string, body []byte, contentType string) error {
if contentType == "" {
contentType = "application/octet-stream"
}
_, err := m.client.PutObject(ctx, m.bucket, key, bytes.NewReader(body), int64(len(body)),
minio.PutObjectOptions{ContentType: contentType})
if err != nil {
return fmt.Errorf("objectstore: put %q: %w", key, err)
}
return nil
}
func (m *minioStore) Get(ctx context.Context, key string) ([]byte, error) {
obj, err := m.client.GetObject(ctx, m.bucket, key, minio.GetObjectOptions{})
if err != nil {
return nil, fmt.Errorf("objectstore: get %q: %w", key, err)
}
defer obj.Close()
maxBytes := int64(env.Int("CAVE_OBJECTSTORE_MAX_READ_BYTES", 64<<20))
if maxBytes <= 0 {
maxBytes = 64 << 20
}
body, err := io.ReadAll(io.LimitReader(obj, maxBytes+1))
if err != nil {
if minio.ToErrorResponse(err).Code == "NoSuchKey" {
return nil, ErrNotFound
}
return nil, fmt.Errorf("objectstore: read %q: %w", key, err)
}
if int64(len(body)) > maxBytes {
return nil, ErrTooLarge
}
return body, nil
}View on GitHub (pinned to 3ee70a1026)