fix(api): descriptive InvalidArgument reason for Windows-unsupported object keys (#4947)

fix(api): return descriptive InvalidArgument reason for Windows-unsupported object keys

On Windows hosts object keys containing NTFS-reserved characters or
Win32-unaddressable path segments are rejected up front, but the client
only saw a bare "Invalid argument" (issue #3299). Attach an explicit
reason to these rejections and surface non-empty InvalidArgument reasons
through the S3 error message.
This commit is contained in:
Zhengchao An
2026-07-17 19:00:52 +08:00
committed by GitHub
parent e279a4f48a
commit e1fc4b12ea
2 changed files with 66 additions and 4 deletions
+27
View File
@@ -276,6 +276,13 @@ impl From<StorageError> for ApiError {
let message = if code == S3ErrorCode::InternalError {
err.to_string()
} else if let StorageError::InvalidArgument(_, _, reason) = &err
&& !reason.is_empty()
{
// Argument-validation rejections carry a client-actionable reason
// (e.g. object keys unsupported on Windows hosts, issue #3299);
// a bare "Invalid argument" gives the caller nothing to fix.
format!("Invalid argument: {reason}")
} else {
ApiError::error_code_to_message(&code)
};
@@ -397,6 +404,26 @@ mod tests {
}
}
#[test]
fn test_api_error_surfaces_invalid_argument_reason() {
let err = StorageError::InvalidArgument(
"bucket".into(),
"a:b*c".into(),
"object key contains characters unsupported on Windows hosts".into(),
);
let api_error: ApiError = err.into();
assert_eq!(api_error.code, S3ErrorCode::InvalidArgument);
assert_eq!(
api_error.message,
"Invalid argument: object key contains characters unsupported on Windows hosts"
);
// An empty reason falls back to the generic message.
let err = StorageError::InvalidArgument("bucket".into(), "object".into(), String::new());
let api_error: ApiError = err.into();
assert_eq!(api_error.message, "Invalid argument");
}
#[test]
fn test_api_error_other_function() {
let custom_error = "Custom API error";