or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

authentication.mdcli-commands.mdconfiguration.mderrors.mdformatters.mdindex.mdmodels.mdprogrammatic.mdscanning.md

errors.mddocs/

0

# Error Handling and Exceptions

1

2

Safety CLI provides a comprehensive error handling system with specific exception types for different failure scenarios. This enables robust error handling in automated environments and clear feedback for developers.

3

4

## Core Exception Classes { .api }

5

6

**Import Statements:**

7

8

```python

9

from safety.errors import (

10

# Base exception classes

11

SafetyError, SafetyException,

12

13

# Database and connectivity errors

14

DatabaseFetchError, DatabaseFileNotFoundError, MalformedDatabase,

15

NetworkConnectionError, RequestTimeoutError, ServerError, TooManyRequestsError,

16

17

# Authentication and validation errors

18

InvalidCredentialError, NotVerifiedEmailError, InvalidRequirementError,

19

InvalidProvidedReportError

20

)

21

from safety.constants import (

22

EXIT_CODE_FAILURE, EXIT_CODE_VULNERABILITIES_FOUND,

23

EXIT_CODE_INVALID_AUTH_CREDENTIAL, EXIT_CODE_INVALID_REQUIREMENT,

24

EXIT_CODE_MALFORMED_DB, EXIT_CODE_TOO_MANY_REQUESTS,

25

EXIT_CODE_UNABLE_TO_FETCH_VULNERABILITY_DB,

26

EXIT_CODE_UNABLE_TO_LOAD_LOCAL_VULNERABILITY_DB,

27

EXIT_CODE_EMAIL_NOT_VERIFIED, EXIT_CODE_INVALID_PROVIDED_REPORT

28

)

29

```

30

31

### SafetyException { .api }

32

33

**Description**: Base exception class for all Safety CLI errors with formatted messaging.

34

35

```python

36

class SafetyException(Exception):

37

"""Base exception for Safety CLI errors with template messaging."""

38

39

def __init__(self,

40

message: str = "Unhandled exception happened: {info}",

41

info: str = "") -> None:

42

"""

43

Initialize Safety exception with formatted message.

44

45

Args:

46

message (str): Error message template with {info} placeholder

47

info (str): Additional information for message template

48

"""

49

50

def get_exit_code(self) -> int:

51

"""

52

Get the exit code associated with this exception.

53

54

Returns:

55

int: Exit code (default: EXIT_CODE_FAILURE = 1)

56

"""

57

```

58

59

**Example Usage:**

60

61

```python

62

from safety.errors import SafetyException

63

64

# Basic exception with default message

65

raise SafetyException(info="Database connection failed")

66

67

# Custom message template

68

raise SafetyException(

69

message="Scan failed for project {info}",

70

info="my-project-v1.0"

71

)

72

73

# Handle exception and get exit code

74

try:

75

# Safety operation

76

pass

77

except SafetyException as e:

78

print(f"Error: {e.message}")

79

exit(e.get_exit_code())

80

```

81

82

### SafetyError { .api }

83

84

**Description**: Generic Safety CLI error with optional error codes.

85

86

```python

87

class SafetyError(Exception):

88

"""Generic Safety CLI error with error code support."""

89

90

def __init__(self,

91

message: str = "Unhandled Safety generic error",

92

error_code: Optional[int] = None) -> None:

93

"""

94

Initialize Safety error.

95

96

Args:

97

message (str): Error description

98

error_code (Optional[int]): Numeric error code for categorization

99

"""

100

101

message: str # Error message

102

error_code: Optional[int] # Optional error code

103

```

104

105

**Example Usage:**

106

107

```python

108

from safety.errors import SafetyError

109

110

# Generic error

111

raise SafetyError("Failed to process vulnerability data")

112

113

# Error with code

114

raise SafetyError(

115

message="API rate limit exceeded",

116

error_code=429

117

)

118

119

# Handle with error code checking

120

try:

121

# Safety operation

122

pass

123

except SafetyError as e:

124

if e.error_code == 429:

125

print("Rate limited - retrying in 60 seconds")

126

else:

127

print(f"Safety error: {e.message}")

128

```

129

130

## Specific Exception Types

131

132

### Authentication Errors { .api }

133

134

#### InvalidCredentialError { .api }

135

136

**Description**: Raised when authentication credentials are invalid or expired.

137

138

```python

139

class InvalidCredentialError(SafetyError):

140

"""Authentication credential validation error."""

141

142

def get_exit_code(self) -> int:

143

"""Returns EXIT_CODE_INVALID_AUTH_CREDENTIAL (2)"""

144

```

145

146

**Common Scenarios:**

147

- Invalid API key

148

- Expired authentication token

149

- Insufficient permissions for organization

150

- Email not verified

151

152

**Example Usage:**

153

154

```python

155

from safety.errors import InvalidCredentialError

156

from safety.auth.utils import SafetyAuthSession

157

158

try:

159

session = SafetyAuthSession()

160

session.api_key = "invalid-key"

161

response = session.get("/user/profile")

162

except InvalidCredentialError as e:

163

print("Authentication failed - please run 'safety auth login'")

164

exit(2)

165

```

166

167

### Network and Connection Errors { .api }

168

169

#### NetworkConnectionError { .api }

170

171

**Description**: Network connectivity and communication errors.

172

173

```python

174

class NetworkConnectionError(SafetyError):

175

"""Network connectivity error."""

176

177

# Common causes:

178

# - No internet connection

179

# - Proxy configuration issues

180

# - DNS resolution failures

181

# - Firewall blocking connections

182

```

183

184

#### RequestTimeoutError { .api }

185

186

**Description**: Request timeout errors for API calls and downloads.

187

188

```python

189

class RequestTimeoutError(SafetyError):

190

"""Request timeout error."""

191

192

# Triggered when:

193

# - API requests exceed timeout limit

194

# - Database downloads are slow

195

# - Network latency is high

196

```

197

198

#### ServerError { .api }

199

200

**Description**: Server-side errors from Safety platform APIs.

201

202

```python

203

class ServerError(SafetyError):

204

"""Server-side error from Safety platform."""

205

206

# Indicates:

207

# - Safety platform maintenance

208

# - Internal server errors (5xx)

209

# - Service unavailability

210

```

211

212

#### TooManyRequestsError { .api }

213

214

**Description**: API rate limiting errors.

215

216

```python

217

class TooManyRequestsError(SafetyError):

218

"""API rate limiting error."""

219

220

def get_exit_code(self) -> int:

221

"""Returns EXIT_CODE_TOO_MANY_REQUESTS (3)"""

222

```

223

224

**Example Usage:**

225

226

```python

227

from safety.errors import (

228

NetworkConnectionError, RequestTimeoutError,

229

ServerError, TooManyRequestsError

230

)

231

import time

232

233

def retry_with_backoff(operation, max_retries=3):

234

"""Retry operation with exponential backoff for network errors."""

235

236

for attempt in range(max_retries):

237

try:

238

return operation()

239

except TooManyRequestsError:

240

if attempt < max_retries - 1:

241

wait_time = 2 ** attempt * 60 # 60s, 120s, 240s

242

print(f"Rate limited - waiting {wait_time}s before retry")

243

time.sleep(wait_time)

244

else:

245

raise

246

except (NetworkConnectionError, RequestTimeoutError) as e:

247

if attempt < max_retries - 1:

248

print(f"Network error - retrying in 10s: {e}")

249

time.sleep(10)

250

else:

251

raise

252

except ServerError:

253

print("Safety platform unavailable - please try again later")

254

raise

255

```

256

257

### Data and Parsing Errors { .api }

258

259

#### InvalidRequirementError { .api }

260

261

**Description**: Requirement parsing and validation errors.

262

263

```python

264

class InvalidRequirementError(SafetyError):

265

"""Requirement parsing error."""

266

267

def get_exit_code(self) -> int:

268

"""Returns EXIT_CODE_INVALID_REQUIREMENT (4)"""

269

```

270

271

**Common Causes:**

272

- Malformed requirement specifications

273

- Invalid version constraints

274

- Unsupported requirement syntax

275

- Missing package names

276

277

**Example Usage:**

278

279

```python

280

from safety.errors import InvalidRequirementError

281

from safety.models import SafetyRequirement

282

283

def parse_requirement_safely(req_string: str) -> Optional[SafetyRequirement]:

284

"""Parse requirement with error handling."""

285

286

try:

287

return SafetyRequirement(req_string)

288

except InvalidRequirementError as e:

289

print(f"Invalid requirement '{req_string}': {e.message}")

290

return None

291

292

# Usage

293

requirements = [

294

"requests>=2.20.0",

295

"django>=3.0,<4.0",

296

"invalid-requirement-syntax!!!" # This will fail

297

]

298

299

valid_requirements = []

300

for req_str in requirements:

301

req = parse_requirement_safely(req_str)

302

if req:

303

valid_requirements.append(req)

304

```

305

306

## Exit Codes { .api }

307

308

Safety CLI uses specific exit codes to indicate different error conditions:

309

310

```python

311

from safety.constants import (

312

EXIT_CODE_OK, # 0 - Success

313

EXIT_CODE_FAILURE, # 1 - General failure

314

EXIT_CODE_INVALID_AUTH_CREDENTIAL, # 2 - Authentication error

315

EXIT_CODE_TOO_MANY_REQUESTS, # 3 - Rate limiting

316

EXIT_CODE_INVALID_REQUIREMENT, # 4 - Requirement parsing error

317

EXIT_CODE_MALFORMED_DB, # 5 - Database corruption

318

EXIT_CODE_UNABLE_TO_FETCH_VULNERABILITY_DB, # 6 - DB download failure

319

EXIT_CODE_UNABLE_TO_LOAD_LOCAL_VULNERABILITY_DB, # 7 - Local DB error

320

EXIT_CODE_EMAIL_NOT_VERIFIED, # 8 - Email verification required

321

EXIT_CODE_INVALID_PROVIDED_REPORT, # 9 - Invalid report format

322

EXIT_CODE_VULNERABILITIES_FOUND # 64 - Vulnerabilities detected

323

)

324

325

# Exit code mapping

326

EXIT_CODE_DESCRIPTIONS = {

327

0: "Success - no issues found",

328

1: "General failure or error",

329

2: "Invalid authentication credentials",

330

3: "Too many requests - rate limited",

331

4: "Invalid requirement specification",

332

5: "Malformed vulnerability database",

333

6: "Unable to fetch vulnerability database",

334

7: "Unable to load local vulnerability database",

335

8: "Email verification required",

336

9: "Invalid report format provided",

337

64: "Vulnerabilities found in dependencies"

338

}

339

```

340

341

## Error Handling Patterns

342

343

### Exception Hierarchy { .api }

344

345

```python

346

# Exception inheritance hierarchy

347

Exception

348

├── SafetyException (base with exit codes)

349

│ └── [Various specific exceptions inherit exit code behavior]

350

└── SafetyError (generic with error codes)

351

├── InvalidCredentialError

352

├── NetworkConnectionError

353

├── RequestTimeoutError

354

├── ServerError

355

├── TooManyRequestsError

356

└── InvalidRequirementError

357

```

358

359

### Comprehensive Error Handling { .api }

360

361

```python

362

from safety.errors import *

363

import logging

364

365

def handle_safety_operation():

366

"""Example of comprehensive error handling for Safety operations."""

367

368

logger = logging.getLogger(__name__)

369

370

try:

371

# Perform Safety operation

372

result = safety_scan_operation()

373

return result

374

375

except InvalidCredentialError as e:

376

logger.error(f"Authentication failed: {e.message}")

377

print("Please run 'safety auth login' to authenticate")

378

return None

379

380

except TooManyRequestsError as e:

381

logger.warning(f"Rate limited: {e.message}")

382

print("API rate limit exceeded - please try again later")

383

return None

384

385

except NetworkConnectionError as e:

386

logger.error(f"Network error: {e.message}")

387

print("Unable to connect to Safety platform - check network connection")

388

return None

389

390

except InvalidRequirementError as e:

391

logger.error(f"Requirement parsing failed: {e.message}")

392

print("Invalid requirement format in dependency files")

393

return None

394

395

except ServerError as e:

396

logger.error(f"Platform error: {e.message}")

397

print("Safety platform is temporarily unavailable")

398

return None

399

400

except SafetyException as e:

401

logger.error(f"Safety exception: {e.message}")

402

print(f"Safety CLI error: {e.message}")

403

exit(e.get_exit_code())

404

405

except SafetyError as e:

406

logger.error(f"Safety error: {e.message}")

407

if e.error_code:

408

print(f"Error {e.error_code}: {e.message}")

409

else:

410

print(f"Error: {e.message}")

411

return None

412

413

except Exception as e:

414

logger.exception("Unexpected error in Safety operation")

415

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

416

exit(EXIT_CODE_FAILURE)

417

```

418

419

### Error Context Management { .api }

420

421

```python

422

from contextlib import contextmanager

423

from safety.errors import SafetyError

424

425

@contextmanager

426

def safety_operation_context(operation_name: str):

427

"""Context manager for Safety operations with error handling."""

428

429

try:

430

print(f"Starting {operation_name}...")

431

yield

432

print(f"✅ {operation_name} completed successfully")

433

434

except SafetyError as e:

435

print(f"❌ {operation_name} failed: {e.message}")

436

if hasattr(e, 'get_exit_code'):

437

exit(e.get_exit_code())

438

else:

439

exit(EXIT_CODE_FAILURE)

440

441

except Exception as e:

442

print(f"❌ {operation_name} failed unexpectedly: {e}")

443

exit(EXIT_CODE_FAILURE)

444

445

# Usage

446

with safety_operation_context("vulnerability scan"):

447

scan_results = perform_vulnerability_scan()

448

449

with safety_operation_context("license check"):

450

license_results = perform_license_check()

451

```

452

453

### Logging Integration { .api }

454

455

```python

456

import logging

457

from safety.errors import SafetyError, SafetyException

458

459

# Configure logging for error tracking

460

logging.basicConfig(

461

level=logging.INFO,

462

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

463

handlers=[

464

logging.FileHandler('safety.log'),

465

logging.StreamHandler()

466

]

467

)

468

469

logger = logging.getLogger('safety.errors')

470

471

def log_and_handle_error(e: Exception, context: str = ""):

472

"""Centralized error logging and handling."""

473

474

if isinstance(e, SafetyException):

475

logger.error(f"Safety exception in {context}: {e.message}")

476

return e.get_exit_code()

477

478

elif isinstance(e, SafetyError):

479

logger.error(f"Safety error in {context}: {e.message} (code: {e.error_code})")

480

return EXIT_CODE_FAILURE

481

482

else:

483

logger.exception(f"Unexpected error in {context}")

484

return EXIT_CODE_FAILURE

485

```

486

487

## CI/CD Error Handling

488

489

### Exit Code Handling in CI/CD { .api }

490

491

```bash

492

#!/bin/bash

493

# CI/CD script with Safety error handling

494

495

set -e # Exit on any error

496

497

safety scan

498

SAFETY_EXIT_CODE=$?

499

500

case $SAFETY_EXIT_CODE in

501

0)

502

echo "✅ No security issues found"

503

;;

504

64)

505

echo "🚨 Vulnerabilities found - failing build"

506

exit 1

507

;;

508

2)

509

echo "❌ Authentication failed - check SAFETY_API_KEY"

510

exit 1

511

;;

512

3)

513

echo "⏳ Rate limited - retrying after delay"

514

sleep 60

515

safety scan

516

;;

517

*)

518

echo "❌ Safety scan failed with exit code $SAFETY_EXIT_CODE"

519

exit 1

520

;;

521

esac

522

```

523

524

### GitHub Actions Error Handling { .api }

525

526

```yaml

527

# .github/workflows/security-scan.yml

528

name: Security Scan

529

530

on: [push, pull_request]

531

532

jobs:

533

security:

534

runs-on: ubuntu-latest

535

steps:

536

- uses: actions/checkout@v3

537

538

- name: Setup Python

539

uses: actions/setup-python@v4

540

with:

541

python-version: '3.11'

542

543

- name: Install Safety

544

run: pip install safety

545

546

- name: Run Security Scan

547

env:

548

SAFETY_API_KEY: ${{ secrets.SAFETY_API_KEY }}

549

run: |

550

set -e

551

safety scan --output json --save-as json:security-report.json || EXIT_CODE=$?

552

553

case ${EXIT_CODE:-0} in

554

0)

555

echo "✅ No security issues found"

556

;;

557

64)

558

echo "🚨 Security vulnerabilities detected"

559

echo "::error::Security vulnerabilities found - see security-report.json"

560

exit 1

561

;;

562

2)

563

echo "::error::Authentication failed - check SAFETY_API_KEY secret"

564

exit 1

565

;;

566

*)

567

echo "::error::Safety scan failed with exit code ${EXIT_CODE:-0}"

568

exit 1

569

;;

570

esac

571

572

- name: Upload Security Report

573

if: always()

574

uses: actions/upload-artifact@v3

575

with:

576

name: security-report

577

path: security-report.json

578

```

579

580

### Additional Error Classes { .api }

581

582

#### MalformedDatabase { .api }

583

584

**Description**: Vulnerability database corruption or format errors.

585

586

```python

587

class MalformedDatabase(SafetyError):

588

"""Malformed vulnerability database error."""

589

590

def get_exit_code(self) -> int:

591

"""Returns EXIT_CODE_MALFORMED_DB (69)"""

592

```

593

594

#### DatabaseFileNotFoundError { .api }

595

596

**Description**: Local vulnerability database file missing or inaccessible.

597

598

```python

599

class DatabaseFileNotFoundError(DatabaseFetchError):

600

"""Local vulnerability database file not found."""

601

```

602

603

#### NotVerifiedEmailError { .api }

604

605

**Description**: Account email verification required for API access.

606

607

```python

608

class NotVerifiedEmailError(SafetyError):

609

"""Email verification required error."""

610

611

def get_exit_code(self) -> int:

612

"""Returns EXIT_CODE_EMAIL_NOT_VERIFIED (72)"""

613

```

614

615

#### InvalidProvidedReportError { .api }

616

617

**Description**: Invalid report format or content provided to Safety.

618

619

```python

620

class InvalidProvidedReportError(SafetyError):

621

"""Invalid report provided error."""

622

623

def get_exit_code(self) -> int:

624

"""Returns EXIT_CODE_INVALID_PROVIDED_REPORT (70)"""

625

```

626

627

This comprehensive error handling documentation provides developers with all the information needed to implement robust error handling when integrating Safety CLI into their applications and automation workflows.