{"record":{"id":"3f23fac38597c5ad","repo":"JuliusBrussee/caveman","slug":"objectstore-stat-q-w","errorCode":null,"errorMessage":"objectstore: stat %q: %w","messagePattern":"objectstore: stat %q: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shared/platform/objectstore/objectstore.go","lineNumber":351,"sourceCode":"\t\tif err := m.client.RemoveObject(ctx, m.bucket, obj.Key, minio.RemoveObjectOptions{VersionID: obj.VersionID}); err != nil {\n\t\t\treturn fmt.Errorf(\"objectstore: delete version %q (%q): %w\", obj.Key, obj.VersionID, err)\n\t\t}\n\t}\n\tif exact && !found {\n\t\t// Non-versioned/S3-compatible backends may omit version listings. Ordinary\n\t\t// deletion remains idempotent and covers that deployment shape.\n\t\treturn m.Delete(ctx, keyOrPrefix)\n\t}\n\treturn nil\n}\n\nfunc (m *minioStore) Exists(ctx context.Context, key string) (bool, error) {\n\t_, err := m.client.StatObject(ctx, m.bucket, key, minio.StatObjectOptions{})\n\tif err != nil {\n\t\tif minio.ToErrorResponse(err).Code == \"NoSuchKey\" {\n\t\t\treturn false, nil\n\t\t}\n\t\treturn false, fmt.Errorf(\"objectstore: stat %q: %w\", key, err)\n\t}\n\treturn true, nil\n}\n\nfunc (m *minioStore) List(ctx context.Context, prefix string) ([]string, error) {\n\tvar keys []string\n\tfor obj := range m.client.ListObjects(ctx, m.bucket, minio.ListObjectsOptions{Prefix: prefix, Recursive: true}) {\n\t\tif obj.Err != nil {\n\t\t\treturn nil, fmt.Errorf(\"objectstore: list %q: %w\", prefix, obj.Err)\n\t\t}\n\t\tkeys = append(keys, obj.Key)\n\t}\n\treturn keys, nil\n}\n\nfunc (m *minioStore) ListPage(ctx context.Context, prefix, startAfter string, maxKeys int) (ObjectPage, error) {\n\tif maxKeys <= 0 {\n\t\treturn ObjectPage{}, fmt.Errorf(\"objectstore: page size must be positive\")","sourceCodeStart":333,"sourceCodeEnd":369,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/shared/platform/objectstore/objectstore.go#L333-L369","documentation":"minioStore.Exists wraps failures from client.StatObject when checking key presence. NoSuchKey is intentionally translated to (false, nil), so this error only appears for anything else: permissions, network problems, bucket mismatches, or backend errors. It keeps the key and underlying error via %w.","triggerScenarios":"Calling Exists(ctx, key) when StatObject returns a non-NoSuchKey error: AccessDenied on the bucket, no such bucket, DNS/connection failure, TLS mismatch, or throttling.","commonSituations":"IAM policy granting List but not GetObject (stat needs read); wrong bucket name after config change; MinIO endpoint unreachable in local dev; request-rate throttling during hot polling loops.","solutions":["Unwrap and check minio.ErrorResponse.Code: NoSuchBucket -> fix bucket config; AccessDenied -> grant s3:GetObject.","Verify the endpoint URL, region, and TLS settings in the store constructor.","Add backoff if Exists is called in a tight polling loop (throttling).","Retry once on transient network errors before surfacing.","Ensure credentials/env (MINIO access/secret) are set for the deployment."],"exampleFix":"// before\nok, err := store.Exists(ctx, key)\nif err != nil {\n    return err\n}\n// after\nok, err := store.Exists(ctx, key)\nif err != nil {\n    var mr minio.ErrorResponse\n    if errors.As(err, &mr) && mr.Code == \"NoSuchBucket\" {\n        return fmt.Errorf(\"bucket %q does not exist\", mr.BucketName)\n    }\n    return fmt.Errorf(\"exists %s: %w\", key, err)\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"ok, err := store.Exists(ctx, key)\nif err != nil {\n    var mr minio.ErrorResponse\n    if errors.As(err, &mr) && (mr.Code == \"NoSuchBucket\" || mr.Code == \"AccessDenied\") {\n        return fmt.Errorf(\"objectstore misconfigured (%s): %w\", mr.Code, err)\n    }\n    return err\n}","preventionTips":["Ensure read (s3:GetObject) not just list permission for existence checks.","Avoid hot Exists polling loops; use backoff or events.","Validate bucket name/endpoint at startup with a canary StatObject.","Never treat a non-NoSuchKey error as 'object missing'."],"tags":["object-storage","stat","minio","permissions"],"backgroundTag":"http-error-response","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}