or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

client-configuration.mdcomments-annotations.mdcommon-types.mddatasets.mdexceptions.mdhealth.mdindex.mdingestion.mdmedia.mdmetrics.mdmodels.mdpagination.mdprojects-organizations.mdprompts.mdscim.mdscores.mdsessions.mdtraces-observations.md

exceptions.mddocs/

0

# Exception Handling

1

2

The Langfuse Java client provides a comprehensive exception hierarchy for error handling. All exceptions extend from base types that include HTTP status codes and response bodies for debugging.

3

4

## Exception Hierarchy

5

6

```

7

RuntimeException

8

└── LangfuseClientException

9

└── LangfuseClientApiException

10

├── Error (400 Bad Request)

11

├── UnauthorizedError (401 Unauthorized)

12

├── AccessDeniedError (403 Forbidden)

13

├── NotFoundError (404 Not Found)

14

├── MethodNotAllowedError (405 Method Not Allowed)

15

└── ServiceUnavailableError (503 Service Unavailable)

16

```

17

18

## Base Exceptions

19

20

### LangfuseClientException

21

22

Base exception for all SDK errors.

23

24

```java { .api }

25

/**

26

* Base exception for Langfuse client errors

27

* Extends RuntimeException

28

*/

29

public class LangfuseClientException extends RuntimeException {

30

/**

31

* Create exception with message

32

*/

33

public LangfuseClientException(String message);

34

35

/**

36

* Create exception with message and cause

37

*/

38

public LangfuseClientException(String message, Exception cause);

39

}

40

```

41

42

### LangfuseClientApiException

43

44

Base exception for non-2XX API responses.

45

46

```java { .api }

47

/**

48

* Exception for non-2XX API responses

49

* Provides access to HTTP status code and response body

50

*/

51

public class LangfuseClientApiException extends LangfuseClientException {

52

/**

53

* Create API exception with status code and body

54

*/

55

public LangfuseClientApiException(String message, int statusCode, Object body);

56

57

/**

58

* Get HTTP status code

59

*/

60

public int statusCode();

61

62

/**

63

* Get response body

64

*/

65

public Object body();

66

67

/**

68

* String representation with status and body

69

*/

70

@Override

71

public String toString();

72

}

73

```

74

75

## HTTP Status Code Exceptions

76

77

### Error (400 Bad Request)

78

79

```java { .api }

80

/**

81

* Generic client error (400 Bad Request)

82

* Thrown for invalid requests

83

*/

84

public class Error extends LangfuseClientApiException {

85

public Error(Object body);

86

87

public int statusCode(); // Returns 400

88

public Object body();

89

}

90

```

91

92

**Common Causes:**

93

- Invalid request parameters

94

- Malformed JSON

95

- Missing required fields

96

- Invalid field values

97

98

### UnauthorizedError (401 Unauthorized)

99

100

```java { .api }

101

/**

102

* Authentication failed (401 Unauthorized)

103

* Thrown when credentials are invalid or missing

104

*/

105

public class UnauthorizedError extends LangfuseClientApiException {

106

public UnauthorizedError(Object body);

107

108

public int statusCode(); // Returns 401

109

public Object body();

110

}

111

```

112

113

**Common Causes:**

114

- Invalid API keys

115

- Expired credentials

116

- Missing Authorization header

117

- Incorrect Basic Auth format

118

119

### AccessDeniedError (403 Forbidden)

120

121

```java { .api }

122

/**

123

* Permission denied (403 Forbidden)

124

* Thrown when authenticated but lacking permissions

125

*/

126

public class AccessDeniedError extends LangfuseClientApiException {

127

public AccessDeniedError(Object body);

128

129

public int statusCode(); // Returns 403

130

public Object body();

131

}

132

```

133

134

**Common Causes:**

135

- Project-scoped key used for organization operations

136

- Insufficient role permissions

137

- Accessing another organization's resources

138

- Attempting to delete Langfuse-managed models

139

140

### NotFoundError (404 Not Found)

141

142

```java { .api }

143

/**

144

* Resource not found (404 Not Found)

145

* Thrown when requested resource doesn't exist

146

*/

147

public class NotFoundError extends LangfuseClientApiException {

148

public NotFoundError(Object body);

149

150

public int statusCode(); // Returns 404

151

public Object body();

152

}

153

```

154

155

**Common Causes:**

156

- Invalid trace/observation/prompt ID

157

- Deleted resource

158

- Typo in resource identifier

159

- Wrong project

160

161

### MethodNotAllowedError (405 Method Not Allowed)

162

163

```java { .api }

164

/**

165

* HTTP method not allowed (405 Method Not Allowed)

166

* Thrown when using wrong HTTP method for endpoint

167

*/

168

public class MethodNotAllowedError extends LangfuseClientApiException {

169

public MethodNotAllowedError(Object body);

170

171

public int statusCode(); // Returns 405

172

public Object body();

173

}

174

```

175

176

**Common Causes:**

177

- Using GET instead of POST

178

- Endpoint doesn't support the HTTP method

179

- Client/server version mismatch

180

181

### ServiceUnavailableError (503 Service Unavailable)

182

183

```java { .api }

184

/**

185

* Service unavailable (503 Service Unavailable)

186

* Thrown by health endpoint when service is down

187

*/

188

public class ServiceUnavailableError extends LangfuseClientApiException {

189

public ServiceUnavailableError(Object body);

190

191

public int statusCode(); // Returns 503

192

public Object body();

193

}

194

```

195

196

**Common Causes:**

197

- Database connection issues

198

- Service maintenance

199

- Temporary outage

200

- Network problems

201

202

## Error Handling Patterns

203

204

### Basic Try-Catch

205

206

```java

207

import com.langfuse.client.LangfuseClient;

208

import com.langfuse.client.core.LangfuseClientApiException;

209

import com.langfuse.client.resources.commons.errors.*;

210

211

try {

212

var prompts = client.prompts().list();

213

} catch (LangfuseClientApiException e) {

214

System.err.println("API Error: " + e.statuscode());

215

System.err.println("Body: " + e.body());

216

}

217

```

218

219

**Important Note on Error Class**: When using wildcard imports (`import com.langfuse.client.resources.commons.errors.*;`), be aware that the `Error` class from this package will conflict with `java.lang.Error`. In catch blocks, use the fully qualified name `com.langfuse.client.resources.commons.errors.Error` to avoid ambiguity.

220

221

### Specific Exception Handling

222

223

```java

224

import com.langfuse.client.resources.commons.errors.*;

225

226

try {

227

Trace trace = client.trace().get("invalid-id");

228

} catch (NotFoundError e) {

229

System.err.println("Trace not found: " + e.body());

230

} catch (UnauthorizedError e) {

231

System.err.println("Authentication failed: " + e.body());

232

} catch (AccessDeniedError e) {

233

System.err.println("Access denied: " + e.body());

234

} catch (LangfuseClientApiException e) {

235

System.err.println("API error: " + e.statusCode());

236

}

237

```

238

239

### Retry Logic

240

241

```java

242

import com.langfuse.client.resources.health.errors.ServiceUnavailableError;

243

244

public <T> T withRetry(Supplier<T> operation, int maxRetries) {

245

int attempts = 0;

246

while (true) {

247

try {

248

return operation.get();

249

} catch (ServiceUnavailableError e) {

250

attempts++;

251

if (attempts >= maxRetries) {

252

throw e;

253

}

254

try {

255

Thread.sleep(1000 * attempts); // Exponential backoff

256

} catch (InterruptedException ie) {

257

Thread.currentThread().interrupt();

258

throw new RuntimeException(ie);

259

}

260

}

261

}

262

}

263

264

// Usage

265

Trace trace = withRetry(() -> client.trace().get("trace-123"), 3);

266

```

267

268

### Graceful Degradation

269

270

```java

271

public class LangfuseService {

272

private final LangfuseClient client;

273

274

public Optional<Prompt> getPrompt(String name) {

275

try {

276

return Optional.of(client.prompts().get(name));

277

} catch (NotFoundError e) {

278

return Optional.empty();

279

} catch (LangfuseClientApiException e) {

280

logger.error("Failed to get prompt: {}", e.statusCode());

281

return Optional.empty();

282

}

283

}

284

}

285

```

286

287

### Error Logging

288

289

```java

290

import org.slf4j.Logger;

291

import org.slf4j.LoggerFactory;

292

293

public class LangfuseErrorHandler {

294

private static final Logger logger = LoggerFactory.getLogger(LangfuseErrorHandler.class);

295

296

public void handleError(LangfuseClientApiException e) {

297

logger.error(

298

"Langfuse API error: status={}, body={}, message={}",

299

e.statusCode(),

300

e.body(),

301

e.getMessage()

302

);

303

304

// Send to error tracking service

305

if (e instanceof UnauthorizedError) {

306

// Alert on auth failures

307

alertAuthFailure(e);

308

} else if (e instanceof ServiceUnavailableError) {

309

// Alert on service outages

310

alertServiceDown(e);

311

}

312

}

313

314

private void alertAuthFailure(LangfuseClientApiException e) {

315

// Implementation

316

}

317

318

private void alertServiceDown(LangfuseClientApiException e) {

319

// Implementation

320

}

321

}

322

```

323

324

## Complete Error Handling Example

325

326

```java

327

import com.langfuse.client.LangfuseClient;

328

import com.langfuse.client.core.*;

329

import com.langfuse.client.resources.commons.errors.*;

330

import com.langfuse.client.resources.health.errors.*;

331

import org.slf4j.Logger;

332

import org.slf4j.LoggerFactory;

333

334

public class RobustLangfuseClient {

335

private static final Logger logger = LoggerFactory.getLogger(RobustLangfuseClient.class);

336

private final LangfuseClient client;

337

338

public RobustLangfuseClient(String url, String publicKey, String secretKey) {

339

this.client = LangfuseClient.builder()

340

.url(url)

341

.credentials(publicKey, secretKey)

342

.timeout(30)

343

.build();

344

}

345

346

public <T> Optional<T> executeWithErrorHandling(

347

Supplier<T> operation,

348

String operationName

349

) {

350

try {

351

T result = operation.get();

352

logger.debug("{} succeeded", operationName);

353

return Optional.of(result);

354

355

} catch (NotFoundError e) {

356

logger.warn("{} - Resource not found: {}",

357

operationName, e.body());

358

return Optional.empty();

359

360

} catch (UnauthorizedError e) {

361

logger.error("{} - Authentication failed: {}",

362

operationName, e.body());

363

throw new RuntimeException("Invalid Langfuse credentials", e);

364

365

} catch (AccessDeniedError e) {

366

logger.error("{} - Access denied: {}",

367

operationName, e.body());

368

throw new RuntimeException("Insufficient permissions", e);

369

370

} catch (com.langfuse.client.resources.commons.errors.Error e) {

371

logger.error("{} - Bad request (400): {}",

372

operationName, e.body());

373

return Optional.empty();

374

375

} catch (MethodNotAllowedError e) {

376

logger.error("{} - Method not allowed (405): {}",

377

operationName, e.body());

378

throw new RuntimeException("API method not allowed", e);

379

380

} catch (ServiceUnavailableError e) {

381

logger.warn("{} - Service unavailable (503): {}",

382

operationName, e.body());

383

return Optional.empty();

384

385

} catch (LangfuseClientApiException e) {

386

logger.error("{} - API error ({}): {}",

387

operationName, e.statusCode(), e.body());

388

return Optional.empty();

389

390

} catch (LangfuseClientException e) {

391

logger.error("{} - Client error: {}",

392

operationName, e.getMessage());

393

return Optional.empty();

394

395

} catch (Exception e) {

396

logger.error("{} - Unexpected error: {}",

397

operationName, e.getMessage(), e);

398

return Optional.empty();

399

}

400

}

401

402

public Optional<Trace> getTrace(String traceId) {

403

return executeWithErrorHandling(

404

() -> client.trace().get(traceId),

405

"getTrace(" + traceId + ")"

406

);

407

}

408

409

public Optional<PromptMetaListResponse> listPrompts() {

410

return executeWithErrorHandling(

411

() -> client.prompts().list(),

412

"listPrompts"

413

);

414

}

415

}

416

```

417

418

## Best Practices

419

420

1. **Specific Exceptions First**: Catch specific exceptions before general ones

421

2. **Log Status Codes**: Always log HTTP status code and response body

422

3. **Graceful Degradation**: Return fallback values when appropriate

423

4. **Retry Logic**: Implement retries for transient failures (503)

424

5. **Don't Retry Auth Errors**: 401/403 errors won't be fixed by retrying

425

6. **Monitor Errors**: Track error rates and types for debugging

426

7. **User-Friendly Messages**: Convert technical errors to user-friendly messages

427

8. **Circuit Breaker**: Implement circuit breaker for repeated failures

428

429

## Common Error Scenarios

430

431

### Invalid API Keys

432

433

```java

434

try {

435

client.prompts().list();

436

} catch (UnauthorizedError e) {

437

System.err.println("Invalid API keys. Please check your credentials.");

438

}

439

```

440

441

### Resource Not Found

442

443

```java

444

try {

445

Trace trace = client.trace().get(traceId);

446

} catch (NotFoundError e) {

447

logger.info("Trace {} does not exist", traceId);

448

}

449

```

450

451

### Permission Denied

452

453

```java

454

try {

455

Project project = client.projects().create(request);

456

} catch (AccessDeniedError e) {

457

System.err.println("Organization-scoped API key required for this operation");

458

}

459

```

460

461

### Service Outage

462

463

```java

464

try {

465

HealthResponse health = client.health().health();

466

} catch (ServiceUnavailableError e) {

467

System.err.println("Langfuse service is temporarily unavailable");

468

// Fall back to cached data or queue operations

469

}

470

```

471

472

## Testing Error Handling

473

474

```java

475

import static org.mockito.Mockito.*;

476

import static org.junit.jupiter.api.Assertions.*;

477

478

@Test

479

public void testHandlesNotFoundError() {

480

LangfuseClient mockClient = mock(LangfuseClient.class);

481

TraceClient mockTraceClient = mock(TraceClient.class);

482

483

when(mockClient.trace()).thenReturn(mockTraceClient);

484

when(mockTraceClient.get("invalid-id"))

485

.thenThrow(new NotFoundError("Trace not found"));

486

487

RobustLangfuseClient client = new RobustLangfuseClient(mockClient);

488

Optional<Trace> result = client.getTrace("invalid-id");

489

490

assertTrue(result.isEmpty());

491

}

492

```

493

494

## Related Documentation

495

496

- [Client Configuration](./client-configuration.md) - Timeout and retry configuration

497

- [Health](./health.md) - ServiceUnavailableError handling

498