or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

admin-operations.mdadvanced-operations.mdclient-operations.mdconfiguration.mdcredentials-auth.mderror-handling.mdindex.md

error-handling.mddocs/

0

# Error Handling

1

2

Comprehensive exception hierarchy for robust error handling across all MinIO operations. The SDK provides specific exception types for different error conditions, enabling precise error handling and debugging.

3

4

## Capabilities

5

6

### Base Exception Classes

7

8

Foundation exception classes providing common functionality for all MinIO-related errors.

9

10

```python { .api }

11

class MinioException(Exception):

12

"""Base exception class for all MinIO operations."""

13

def __init__(self, message: str) -> None:

14

"""

15

Initialize MinIO exception.

16

17

Args:

18

message: Error message describing the exception

19

"""

20

21

def __str__(self) -> str:

22

"""Return string representation of the exception."""

23

```

24

25

### S3 Operation Errors

26

27

Exceptions for S3 API operation failures with detailed error information from the server.

28

29

```python { .api }

30

class S3Error(MinioException):

31

"""Exception raised when S3 operation fails with error response."""

32

def __init__(

33

self,

34

code: str,

35

message: str,

36

resource: str | None = None,

37

request_id: str | None = None,

38

host_id: str | None = None,

39

response: urllib3.HTTPResponse | None = None,

40

bucket_name: str | None = None,

41

object_name: str | None = None

42

) -> None:

43

"""

44

Initialize S3 error.

45

46

Args:

47

code: S3 error code (e.g., "NoSuchBucket", "AccessDenied")

48

message: Human-readable error message

49

resource: S3 resource that caused the error

50

request_id: AWS request ID for debugging

51

host_id: AWS host ID for debugging

52

response: Original HTTP response object

53

bucket_name: Bucket involved in the operation

54

object_name: Object involved in the operation

55

"""

56

57

code: str

58

message: str

59

resource: str | None

60

request_id: str | None

61

host_id: str | None

62

response: urllib3.HTTPResponse | None

63

bucket_name: str | None

64

object_name: str | None

65

66

@classmethod

67

def fromxml(cls, response: urllib3.HTTPResponse) -> S3Error:

68

"""

69

Create S3Error from XML error response.

70

71

Args:

72

response: HTTP response containing XML error

73

74

Returns:

75

S3Error instance parsed from XML

76

"""

77

78

def copy(self, code: str | None = None, message: str | None = None) -> S3Error:

79

"""

80

Create copy of S3Error with modified code/message.

81

82

Args:

83

code: New error code (optional)

84

message: New error message (optional)

85

86

Returns:

87

New S3Error instance with updated values

88

"""

89

```

90

91

### HTTP Server Errors

92

93

Exceptions for HTTP-level errors including network issues and server failures.

94

95

```python { .api }

96

class ServerError(MinioException):

97

"""Exception raised when S3 service returns HTTP server error."""

98

def __init__(self, message: str, status_code: int) -> None:

99

"""

100

Initialize server error.

101

102

Args:

103

message: Error message

104

status_code: HTTP status code (5xx series)

105

"""

106

107

status_code: int

108

109

def __str__(self) -> str:

110

"""Return formatted error message with status code."""

111

```

112

113

### Response Format Errors

114

115

Exceptions for malformed or unexpected server responses.

116

117

```python { .api }

118

class InvalidResponseError(MinioException):

119

"""Exception raised when server returns non-XML response."""

120

def __init__(

121

self,

122

code: int | None = None,

123

content_type: str | None = None,

124

body: str | None = None

125

) -> None:

126

"""

127

Initialize invalid response error.

128

129

Args:

130

code: HTTP status code

131

content_type: Response content type

132

body: Response body content

133

"""

134

135

code: int | None

136

content_type: str | None

137

body: str | None

138

```

139

140

### Admin Operation Errors

141

142

Exceptions specific to MinIO administrative operations.

143

144

```python { .api }

145

class MinioAdminException(MinioException):

146

"""Exception raised for MinIO admin API execution errors."""

147

def __init__(self, code: int, body: str) -> None:

148

"""

149

Initialize admin exception.

150

151

Args:

152

code: HTTP status code

153

body: Response body containing error details

154

"""

155

156

code: int

157

body: str

158

```

159

160

## Common S3 Error Codes

161

162

The SDK handles numerous standard S3 error codes. Understanding these helps in implementing appropriate error handling strategies.

163

164

### Access and Authentication Errors

165

166

```python

167

# Common error codes for access issues:

168

# - "AccessDenied": Insufficient permissions

169

# - "InvalidAccessKeyId": Invalid access key

170

# - "SignatureDoesNotMatch": Invalid secret key or signature

171

# - "TokenRefreshRequired": STS token needs refresh

172

# - "ExpiredToken": STS token has expired

173

```

174

175

### Bucket-Related Errors

176

177

```python

178

# Common bucket operation error codes:

179

# - "NoSuchBucket": Bucket doesn't exist

180

# - "BucketAlreadyExists": Bucket name already taken

181

# - "BucketNotEmpty": Cannot delete non-empty bucket

182

# - "InvalidBucketName": Bucket name violates naming rules

183

```

184

185

### Object-Related Errors

186

187

```python

188

# Common object operation error codes:

189

# - "NoSuchKey": Object doesn't exist

190

# - "InvalidObjectName": Object name violates naming rules

191

# - "EntityTooLarge": Object exceeds size limits

192

# - "InvalidPart": Multipart upload part is invalid

193

# - "InvalidPartOrder": Multipart parts not in order

194

```

195

196

### Precondition and Constraint Errors

197

198

```python

199

# Common constraint error codes:

200

# - "PreconditionFailed": If-Match/If-None-Match condition failed

201

# - "NotModified": Object not modified since specified date

202

# - "InvalidRange": Byte range is invalid

203

# - "RequestTimeout": Request took too long

204

```

205

206

## Usage Examples

207

208

### Basic Error Handling

209

210

```python

211

from minio import Minio, S3Error, ServerError, InvalidResponseError

212

213

client = Minio("localhost:9000", "minio", "minio123")

214

215

try:

216

# Attempt bucket operation

217

client.make_bucket("my-bucket")

218

print("Bucket created successfully")

219

220

except S3Error as e:

221

if e.code == "BucketAlreadyExists":

222

print("Bucket already exists, continuing...")

223

elif e.code == "AccessDenied":

224

print("Access denied. Check credentials and permissions.")

225

else:

226

print(f"S3 error: {e.code} - {e.message}")

227

228

except ServerError as e:

229

print(f"Server error {e.status_code}: {e}")

230

231

except InvalidResponseError as e:

232

print(f"Invalid response: {e.code} - {e.content_type}")

233

234

except Exception as e:

235

print(f"Unexpected error: {e}")

236

```

237

238

### Specific Error Code Handling

239

240

```python

241

def safe_bucket_operations(client: Minio, bucket_name: str):

242

"""Demonstrate handling specific S3 error scenarios."""

243

244

try:

245

# Check if bucket exists

246

if not client.bucket_exists(bucket_name):

247

client.make_bucket(bucket_name)

248

print(f"Created bucket: {bucket_name}")

249

else:

250

print(f"Bucket {bucket_name} already exists")

251

252

except S3Error as e:

253

if e.code == "NoSuchBucket":

254

print("Bucket doesn't exist and cannot be created")

255

elif e.code == "BucketAlreadyOwnedByYou":

256

print("Bucket already owned by you")

257

elif e.code == "InvalidBucketName":

258

print(f"Invalid bucket name: {bucket_name}")

259

else:

260

print(f"Bucket operation failed: {e.code} - {e.message}")

261

raise

262

263

try:

264

# List objects in bucket

265

objects = client.list_objects(bucket_name)

266

for obj in objects:

267

print(f"Object: {obj.object_name}")

268

269

except S3Error as e:

270

if e.code == "AccessDenied":

271

print("Cannot list objects: access denied")

272

else:

273

print(f"List operation failed: {e.code}")

274

raise

275

```

276

277

### Multipart Upload Error Handling

278

279

```python

280

import io

281

from minio.error import S3Error

282

283

def robust_multipart_upload(client: Minio, bucket_name: str, object_name: str, file_path: str):

284

"""Handle errors in multipart upload with proper cleanup."""

285

286

upload_id = None

287

try:

288

# Initiate multipart upload

289

upload_id = client.initiate_multipart_upload(bucket_name, object_name)

290

291

parts = []

292

part_number = 1

293

294

with open(file_path, 'rb') as f:

295

while True:

296

chunk = f.read(5 * 1024 * 1024) # 5MB chunks

297

if not chunk:

298

break

299

300

try:

301

part = client.upload_part(

302

bucket_name, object_name, upload_id,

303

part_number, io.BytesIO(chunk), len(chunk)

304

)

305

parts.append(part)

306

part_number += 1

307

308

except S3Error as e:

309

if e.code == "InvalidPart":

310

print(f"Invalid part {part_number}, retrying...")

311

continue # Retry this part

312

else:

313

raise # Re-raise for cleanup

314

315

# Complete upload

316

result = client.complete_multipart_upload(

317

bucket_name, object_name, upload_id, parts

318

)

319

print(f"Upload completed: {result.etag}")

320

return result

321

322

except S3Error as e:

323

print(f"Multipart upload failed: {e.code} - {e.message}")

324

325

# Cleanup incomplete upload

326

if upload_id:

327

try:

328

client.abort_multipart_upload(bucket_name, object_name, upload_id)

329

print("Aborted incomplete multipart upload")

330

except S3Error as abort_error:

331

print(f"Failed to abort upload: {abort_error.code}")

332

333

raise

334

335

except Exception as e:

336

print(f"Unexpected error during upload: {e}")

337

338

# Cleanup on any error

339

if upload_id:

340

try:

341

client.abort_multipart_upload(bucket_name, object_name, upload_id)

342

except:

343

pass # Best effort cleanup

344

345

raise

346

```

347

348

### Credential and Authentication Error Handling

349

350

```python

351

from minio.credentials import ChainedProvider, EnvAWSProvider, StaticProvider

352

353

def create_resilient_client(endpoint: str) -> Minio:

354

"""Create client with robust credential handling."""

355

356

# Try multiple credential sources

357

providers = [

358

EnvAWSProvider(),

359

StaticProvider("fallback-key", "fallback-secret")

360

]

361

362

credential_chain = ChainedProvider(providers)

363

364

try:

365

client = Minio(endpoint, credentials=credential_chain)

366

367

# Test credentials by listing buckets

368

buckets = client.list_buckets()

369

print(f"Successfully authenticated, found {len(buckets)} buckets")

370

return client

371

372

except S3Error as e:

373

if e.code in ["AccessDenied", "InvalidAccessKeyId", "SignatureDoesNotMatch"]:

374

print(f"Authentication failed: {e.code}")

375

print("Please check your credentials")

376

elif e.code == "ExpiredToken":

377

print("STS token has expired, refresh required")

378

else:

379

print(f"Authentication error: {e.code} - {e.message}")

380

raise

381

382

except Exception as e:

383

print(f"Client creation failed: {e}")

384

raise

385

```

386

387

### Admin Operation Error Handling

388

389

```python

390

from minio import MinioAdmin

391

from minio.error import MinioAdminException

392

from minio.credentials import StaticProvider

393

394

def safe_admin_operations():

395

"""Handle MinIO admin operation errors."""

396

397

try:

398

admin = MinioAdmin(

399

"localhost:9000",

400

credentials=StaticProvider("admin", "password")

401

)

402

403

# Add user with error handling

404

try:

405

result = admin.user_add("newuser", "password123")

406

print(f"User created: {result}")

407

408

except MinioAdminException as e:

409

if e.code == 409: # Conflict - user already exists

410

print("User already exists")

411

elif e.code == 403: # Forbidden - insufficient permissions

412

print("Insufficient permissions to create user")

413

else:

414

print(f"User creation failed: {e.code} - {e.body}")

415

416

# Set policy with error handling

417

try:

418

policy_json = '{"Version": "2012-10-17", "Statement": []}'

419

admin.policy_add("test-policy", policy_json)

420

421

except MinioAdminException as e:

422

if e.code == 409:

423

print("Policy already exists")

424

else:

425

print(f"Policy creation failed: {e.code}")

426

427

except Exception as e:

428

print(f"Admin client error: {e}")

429

```

430

431

### Retry Logic with Exponential Backoff

432

433

```python

434

import time

435

import random

436

from typing import Callable, TypeVar

437

438

T = TypeVar('T')

439

440

def retry_with_backoff(

441

func: Callable[[], T],

442

max_retries: int = 3,

443

base_delay: float = 1.0,

444

max_delay: float = 60.0

445

) -> T:

446

"""Retry function with exponential backoff for transient errors."""

447

448

for attempt in range(max_retries + 1):

449

try:

450

return func()

451

452

except S3Error as e:

453

# Don't retry on client errors (4xx)

454

if e.code in ["AccessDenied", "NoSuchBucket", "InvalidBucketName"]:

455

raise

456

457

# Retry on server errors and throttling

458

if attempt < max_retries and e.code in ["InternalError", "ServiceUnavailable", "SlowDown"]:

459

delay = min(base_delay * (2 ** attempt) + random.uniform(0, 1), max_delay)

460

print(f"Retrying after {delay:.2f}s (attempt {attempt + 1}/{max_retries + 1})")

461

time.sleep(delay)

462

continue

463

464

raise

465

466

except ServerError as e:

467

# Retry on 5xx server errors

468

if attempt < max_retries and e.status_code >= 500:

469

delay = min(base_delay * (2 ** attempt) + random.uniform(0, 1), max_delay)

470

print(f"Server error, retrying after {delay:.2f}s")

471

time.sleep(delay)

472

continue

473

474

raise

475

476

except Exception as e:

477

# Don't retry on unexpected errors

478

raise

479

480

# Usage example

481

def upload_with_retry(client: Minio, bucket: str, object_name: str, data: bytes):

482

"""Upload with automatic retry on transient errors."""

483

484

def upload_operation():

485

return client.put_object(

486

bucket,

487

object_name,

488

io.BytesIO(data),

489

len(data)

490

)

491

492

try:

493

result = retry_with_backoff(upload_operation, max_retries=3)

494

print(f"Upload successful: {result.etag}")

495

return result

496

except Exception as e:

497

print(f"Upload failed after retries: {e}")

498

raise

499

```

500

501

### Comprehensive Error Logging

502

503

```python

504

import logging

505

from datetime import datetime

506

507

# Configure logging

508

logging.basicConfig(

509

level=logging.INFO,

510

format='%(asctime)s - %(levelname)s - %(message)s'

511

)

512

logger = logging.getLogger(__name__)

513

514

def logged_operation(operation_name: str, func: Callable, *args, **kwargs):

515

"""Execute operation with comprehensive error logging."""

516

517

start_time = datetime.now()

518

logger.info(f"Starting {operation_name}")

519

520

try:

521

result = func(*args, **kwargs)

522

duration = (datetime.now() - start_time).total_seconds()

523

logger.info(f"Completed {operation_name} in {duration:.2f}s")

524

return result

525

526

except S3Error as e:

527

duration = (datetime.now() - start_time).total_seconds()

528

logger.error(

529

f"S3Error in {operation_name} after {duration:.2f}s: "

530

f"Code={e.code}, Message={e.message}, "

531

f"Bucket={e.bucket_name}, Object={e.object_name}, "

532

f"RequestId={e.request_id}"

533

)

534

raise

535

536

except ServerError as e:

537

duration = (datetime.now() - start_time).total_seconds()

538

logger.error(

539

f"ServerError in {operation_name} after {duration:.2f}s: "

540

f"Status={e.status_code}, Message={e}"

541

)

542

raise

543

544

except MinioAdminException as e:

545

duration = (datetime.now() - start_time).total_seconds()

546

logger.error(

547

f"AdminError in {operation_name} after {duration:.2f}s: "

548

f"Code={e.code}, Body={e.body}"

549

)

550

raise

551

552

except Exception as e:

553

duration = (datetime.now() - start_time).total_seconds()

554

logger.error(

555

f"UnexpectedError in {operation_name} after {duration:.2f}s: "

556

f"Type={type(e).__name__}, Message={e}"

557

)

558

raise

559

560

# Usage

561

client = Minio("localhost:9000", "minio", "minio123")

562

563

logged_operation(

564

"bucket_creation",

565

client.make_bucket,

566

"test-bucket"

567

)

568

569

logged_operation(

570

"object_upload",

571

client.put_object,

572

"test-bucket",

573

"test-object.txt",

574

io.BytesIO(b"test data"),

575

9

576

)

577

```