@@ -741,6 +741,20 @@ void S3FileHandle::Initialize(optional_ptr<FileOpener> opener) {
741741 return ;
742742 }
743743 }
744+ auto &extra_info = error.ExtraInfo ();
745+ auto entry = extra_info.find (" status_code" );
746+ if (entry != extra_info.end ()) {
747+ if (entry->second == " 400" ) {
748+ // 400: BAD REQUEST
749+ auto extra_text = S3FileSystem::GetS3BadRequestError (auth_params);
750+ throw Exception (error.Type (), error.RawMessage () + extra_text, extra_info);
751+ }
752+ if (entry->second == " 403" ) {
753+ // 403: FORBIDDEN
754+ auto extra_text = S3FileSystem::GetS3AuthError (auth_params);
755+ throw Exception (error.Type (), error.RawMessage () + extra_text, extra_info);
756+ }
757+ }
744758 throw ;
745759 }
746760
@@ -973,26 +987,43 @@ bool S3FileSystem::ListFiles(const string &directory, const std::function<void(c
973987 return true ;
974988}
975989
976- HTTPException S3FileSystem::GetS3Error (S3AuthParams &s3_auth_params, const HTTPResponse &response, const string &url) {
977- string region = s3_auth_params.region ;
990+ string S3FileSystem::GetS3BadRequestError (S3AuthParams &s3_auth_params) {
991+ string extra_text = " \n\n Bad Request - this can be caused by the S3 region being set incorrectly." ;
992+ if (s3_auth_params.region .empty ()) {
993+ extra_text += " \n * No region is provided." ;
994+ } else {
995+ extra_text += " \n * Provided region is \" " + s3_auth_params.region + " \" " ;
996+ }
997+ return extra_text;
998+ }
999+
1000+ string S3FileSystem::GetS3AuthError (S3AuthParams &s3_auth_params) {
9781001 string extra_text = " \n\n Authentication Failure - this is usually caused by invalid or missing credentials." ;
9791002 if (s3_auth_params.secret_access_key .empty () && s3_auth_params.access_key_id .empty ()) {
9801003 extra_text += " \n * No credentials are provided." ;
9811004 } else {
9821005 extra_text += " \n * Credentials are provided, but they did not work." ;
9831006 }
9841007 extra_text += " \n * See https://duckdb.org/docs/stable/extensions/httpfs/s3api.html" ;
1008+ return extra_text;
1009+ }
1010+
1011+ HTTPException S3FileSystem::GetS3Error (S3AuthParams &s3_auth_params, const HTTPResponse &response, const string &url) {
1012+ string extra_text;
1013+ if (response.status == HTTPStatusCode::BadRequest_400) {
1014+ extra_text = GetS3BadRequestError (s3_auth_params);
1015+ }
1016+ if (response.status == HTTPStatusCode::Forbidden_403) {
1017+ extra_text = GetS3AuthError (s3_auth_params);
1018+ }
9851019 auto status_message = HTTPFSUtil::GetStatusMessage (response.status );
9861020 throw HTTPException (response, " HTTP GET error reading '%s' in region '%s' (HTTP %d %s)%s" , url,
9871021 s3_auth_params.region , response.status , status_message, extra_text);
9881022}
9891023
9901024HTTPException S3FileSystem::GetHTTPError (FileHandle &handle, const HTTPResponse &response, const string &url) {
991- if (response.status == HTTPStatusCode::Forbidden_403) {
992- auto &s3_handle = handle.Cast <S3FileHandle>();
993- return GetS3Error (s3_handle.auth_params , response, url);
994- }
995- return HTTPFileSystem::GetHTTPError (handle, response, url);
1025+ auto &s3_handle = handle.Cast <S3FileHandle>();
1026+ return GetS3Error (s3_handle.auth_params , response, url);
9961027}
9971028string AWSListObjectV2::Request (string &path, HTTPParams &http_params, S3AuthParams &s3_auth_params,
9981029 string &continuation_token, optional_ptr<HTTPState> state, bool use_delimiter) {
0 commit comments