s3: return NoSuchVersion (not NoSuchKey) for a missing versionId (#9749)

GET/HEAD object with an explicit versionId that does not exist returned
NoSuchKey. AWS S3 returns NoSuchVersion (404) for this case; tools that
distinguish "key gone" from "this version gone" rely on that code.

Add the ErrNoSuchVersion error code and use it on the GET and HEAD
specific-version lookups. Only a genuine not-found maps to NoSuchVersion;
a transient or internal filer error now maps to InternalError (500)
instead of a misleading 404. getSpecificObjectVersion wraps its lookup
error with %w so callers can detect filer_pb.ErrNotFound.
This commit is contained in:
Chris Lu
2026-05-30 21:09:53 -07:00
committed by GitHub
parent 7c5ca01027
commit 0e35235908
3 changed files with 18 additions and 4 deletions
+10 -2
View File
@@ -641,7 +641,11 @@ func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request)
entry, err = s3a.getSpecificObjectVersion(bucket, object, versionId)
if err != nil {
glog.Errorf("Failed to get specific version %s: %v", versionId, err)
s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchKey)
if errors.Is(err, filer_pb.ErrNotFound) {
s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchVersion)
} else {
s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
}
return
}
targetVersionId = versionId
@@ -2149,7 +2153,11 @@ func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request
entry, err = s3a.getSpecificObjectVersion(bucket, object, versionId)
if err != nil {
glog.Errorf("Failed to get specific version %s for %s/%s: %v", versionId, bucket, object, err)
s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchKey)
if errors.Is(err, filer_pb.ErrNotFound) {
s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchVersion)
} else {
s3err.WriteErrorResponse(w, r, s3err.ErrInternalError)
}
return
}
targetVersionId = versionId
+2 -2
View File
@@ -984,7 +984,7 @@ func (s3a *S3ApiServer) getSpecificObjectVersion(bucket, object, versionId strin
bucketDir := s3a.bucketDir(bucket)
entry, err := s3a.getEntry(bucketDir, normalizedObject)
if err != nil {
return nil, fmt.Errorf("null version object %s not found: %v", normalizedObject, err)
return nil, fmt.Errorf("null version object %s not found: %w", normalizedObject, err)
}
return entry, nil
}
@@ -995,7 +995,7 @@ func (s3a *S3ApiServer) getSpecificObjectVersion(bucket, object, versionId strin
entry, err := s3a.getEntry(versionsDir, versionFile)
if err != nil {
return nil, fmt.Errorf("version %s not found: %v", versionId, err)
return nil, fmt.Errorf("version %s not found: %w", versionId, err)
}
return entry, nil
+6
View File
@@ -57,6 +57,7 @@ const (
ErrNoSuchCORSConfiguration
ErrNoSuchLifecycleConfiguration
ErrNoSuchKey
ErrNoSuchVersion
ErrNoSuchUpload
ErrInvalidBucketName
ErrInvalidBucketState
@@ -281,6 +282,11 @@ var errorCodeResponse = map[ErrorCode]APIError{
Description: "The specified key does not exist.",
HTTPStatusCode: http.StatusNotFound,
},
ErrNoSuchVersion: {
Code: "NoSuchVersion",
Description: "The specified version does not exist.",
HTTPStatusCode: http.StatusNotFound,
},
ErrNoSuchUpload: {
Code: "NoSuchUpload",
Description: "The specified multipart upload does not exist. The upload ID may be invalid, or the upload may have been aborted or completed.",