or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

comments-attachments.mdcore-client.mderror-handling.mdindex.mdissue-management.mdpagination-connections.mdproject-management.mdteam-user-management.mdwebhook-processing.mdworkflow-cycle-management.md

error-handling.mddocs/

0

# Error Handling

1

2

Comprehensive error handling system with Linear-specific error types, rate limiting support, and structured error information for robust API integration.

3

4

## Capabilities

5

6

### Error Types & Classification

7

8

Linear SDK provides a hierarchy of error types for different failure scenarios.

9

10

```typescript { .api }

11

/**

12

* Base error class for all Linear API errors

13

*/

14

class LinearError extends Error {

15

/** The type of the first error returned by the Linear API */

16

type?: LinearErrorType;

17

/** A list of GraphQL errors returned by the Linear API */

18

errors?: LinearGraphQLError[];

19

/** The GraphQL query that caused this error */

20

query?: string;

21

/** The GraphQL variables that caused this error */

22

variables?: Record<string, unknown>;

23

/** Any data returned by this request */

24

data?: unknown;

25

/** The HTTP status of this request */

26

status?: number;

27

/** The raw LinearGraphQLClient error */

28

raw?: LinearErrorRaw;

29

30

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[], type?: LinearErrorType);

31

}

32

33

/**

34

* Individual GraphQL error representation

35

*/

36

class LinearGraphQLError {

37

/** The type of this GraphQL error */

38

type: LinearErrorType;

39

/** A friendly error message */

40

message: string;

41

/** If this error is caused by the user input */

42

userError?: boolean;

43

/** The path to the GraphQL node at which the error occurred */

44

path?: string[];

45

46

constructor(error?: LinearGraphQLErrorRaw);

47

}

48

49

/**

50

* Enumeration of all error types returned by the Linear API

51

*/

52

enum LinearErrorType {

53

FeatureNotAccessible = "FeatureNotAccessible",

54

InvalidInput = "InvalidInput",

55

Ratelimited = "Ratelimited",

56

NetworkError = "NetworkError",

57

AuthenticationError = "AuthenticationError",

58

Forbidden = "Forbidden",

59

BootstrapError = "BootstrapError",

60

Unknown = "Unknown",

61

InternalError = "InternalError",

62

Other = "Other",

63

UserError = "UserError",

64

GraphqlError = "GraphqlError",

65

LockTimeout = "LockTimeout",

66

UsageLimitExceeded = "UsageLimitExceeded"

67

}

68

```

69

70

### Specific Error Classes

71

72

Each error type has a dedicated class with specific properties and behavior.

73

74

```typescript { .api }

75

/**

76

* Authentication-related errors

77

*/

78

class AuthenticationLinearError extends LinearError {

79

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);

80

}

81

82

/**

83

* Permission-related errors

84

*/

85

class ForbiddenLinearError extends LinearError {

86

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);

87

}

88

89

/**

90

* Input validation errors

91

*/

92

class InvalidInputLinearError extends LinearError {

93

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);

94

}

95

96

/**

97

* Rate limiting errors with detailed rate limit information

98

*/

99

class RatelimitedLinearError extends LinearError {

100

/** How long, in seconds, to wait before making a follow-up request */

101

retryAfter: number | undefined;

102

/** The max amount of requests allowed in the duration */

103

requestsLimit: number | undefined;

104

/** The remaining requests before rate limiting */

105

requestsRemaining: number | undefined;

106

/** Unix timestamp at which the requests will be reset */

107

requestsResetAt: number | undefined;

108

/** The max amount of complexity allowed in the duration */

109

complexityLimit: number | undefined;

110

/** The remaining complexity before rate limiting */

111

complexityRemaining: number | undefined;

112

/** Unix timestamp at which the complexity will be reset */

113

complexityResetAt: number | undefined;

114

115

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);

116

}

117

118

/**

119

* Network connectivity errors

120

*/

121

class NetworkLinearError extends LinearError {

122

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);

123

}

124

125

/**

126

* Feature access restrictions

127

*/

128

class FeatureNotAccessibleLinearError extends LinearError {

129

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);

130

}

131

132

/**

133

* Internal server errors

134

*/

135

class InternalLinearError extends LinearError {

136

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);

137

}

138

139

/**

140

* Usage limit exceeded errors

141

*/

142

class UsageLimitExceededLinearError extends LinearError {

143

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);

144

}

145

146

/**

147

* Database lock timeout errors

148

*/

149

class LockTimeoutLinearError extends LinearError {

150

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);

151

}

152

153

/**

154

* Bootstrap initialization errors

155

*/

156

class BootstrapLinearError extends LinearError {

157

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);

158

}

159

160

/**

161

* Other unclassified errors

162

*/

163

class OtherLinearError extends LinearError {

164

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);

165

}

166

167

/**

168

* User-related errors

169

*/

170

class UserLinearError extends LinearError {

171

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);

172

}

173

174

/**

175

* GraphQL-specific errors

176

*/

177

class GraphqlLinearError extends LinearError {

178

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);

179

}

180

181

/**

182

* Unknown or unclassified errors

183

*/

184

class UnknownLinearError extends LinearError {

185

constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);

186

}

187

```

188

189

### Error Parsing & Handling

190

191

Utility functions for parsing and handling errors from the Linear API.

192

193

```typescript { .api }

194

/**

195

* Parse raw errors from Linear API into typed error instances

196

* @param error - Raw error from LinearGraphQLClient or existing LinearError

197

* @returns Properly typed LinearError instance

198

*/

199

function parseLinearError(error?: LinearErrorRaw | LinearError): LinearError;

200

```

201

202

**Usage Examples:**

203

204

```typescript

205

import { LinearClient, LinearError, LinearErrorType, RatelimitedLinearError } from "@linear/sdk";

206

207

const client = new LinearClient({ apiKey: "your-api-key" });

208

209

try {

210

const issues = await client.issues({ first: 100 });

211

} catch (error) {

212

if (error instanceof LinearError) {

213

console.log("Error type:", error.type);

214

console.log("Error message:", error.message);

215

console.log("HTTP status:", error.status);

216

console.log("GraphQL query:", error.query);

217

218

// Handle specific error types

219

switch (error.type) {

220

case LinearErrorType.AuthenticationError:

221

console.log("Authentication failed - check your API key");

222

break;

223

224

case LinearErrorType.Ratelimited:

225

if (error instanceof RatelimitedLinearError) {

226

console.log("Rate limited - retry after:", error.retryAfter);

227

console.log("Requests remaining:", error.requestsRemaining);

228

console.log("Rate limit resets at:", new Date(error.requestsResetAt! * 1000));

229

}

230

break;

231

232

case LinearErrorType.Forbidden:

233

console.log("Access forbidden - insufficient permissions");

234

break;

235

236

case LinearErrorType.InvalidInput:

237

console.log("Invalid input provided");

238

if (error.errors) {

239

error.errors.forEach(graphqlError => {

240

console.log("Field error:", graphqlError.path, graphqlError.message);

241

});

242

}

243

break;

244

245

default:

246

console.log("Unexpected error occurred");

247

}

248

}

249

}

250

```

251

252

### Error Response Types

253

254

Type definitions for raw error responses and contexts.

255

256

```typescript { .api }

257

/**

258

* Raw error returned by the Linear API

259

*/

260

interface LinearErrorRaw {

261

/** Error name if available */

262

name?: string;

263

/** Error message if available */

264

message?: string;

265

/** Error information for the request */

266

request?: GraphQLRequestContext<Record<string, unknown>>;

267

/** Error information for the response */

268

response?: LinearRawResponse<unknown>;

269

}

270

271

/**

272

* One of potentially many raw GraphQL errors returned by the Linear API

273

*/

274

interface LinearGraphQLErrorRaw {

275

/** The error type */

276

message?: LinearErrorType;

277

/** The path to the GraphQL node at which the error occurred */

278

path?: string[];

279

extensions?: {

280

/** The error type */

281

type?: LinearErrorType;

282

/** If caused by the user input */

283

userError?: boolean;

284

/** A friendly error message */

285

userPresentableMessage?: string;

286

};

287

}

288

289

/**

290

* The raw response from the Linear GraphQL Client

291

*/

292

interface LinearRawResponse<Data> {

293

/** The returned data */

294

data?: Data;

295

/** Any extensions returned by the Linear API */

296

extensions?: unknown;

297

/** Response headers */

298

headers?: Headers;

299

/** Response status */

300

status?: number;

301

/** An error message */

302

error?: string;

303

/** Any GraphQL errors returned by the Linear API */

304

errors?: LinearGraphQLErrorRaw[];

305

}

306

307

/**

308

* Description of a GraphQL request used in error handling

309

*/

310

interface GraphQLRequestContext<Variables extends Record<string, unknown>> {

311

query: string;

312

variables?: Variables;

313

}

314

```

315

316

### Best Practices

317

318

**Error Handling Strategy:**

319

320

```typescript

321

import { LinearClient, LinearError, LinearErrorType } from "@linear/sdk";

322

323

async function handleLinearRequest<T>(requestFn: () => Promise<T>): Promise<T | null> {

324

try {

325

return await requestFn();

326

} catch (error) {

327

if (error instanceof LinearError) {

328

// Log structured error information

329

console.error("Linear API Error:", {

330

type: error.type,

331

message: error.message,

332

status: error.status,

333

query: error.query?.substring(0, 100), // Truncate for logging

334

timestamp: new Date().toISOString()

335

});

336

337

// Handle retryable errors

338

if (error.type === LinearErrorType.Ratelimited && error instanceof RatelimitedLinearError) {

339

if (error.retryAfter && error.retryAfter < 60) {

340

console.log(`Rate limited, retrying in ${error.retryAfter} seconds`);

341

await new Promise(resolve => setTimeout(resolve, error.retryAfter! * 1000));

342

return handleLinearRequest(requestFn); // Retry once

343

}

344

}

345

346

// Handle authentication errors

347

if (error.type === LinearErrorType.AuthenticationError) {

348

throw new Error("Linear authentication failed - please check your API credentials");

349

}

350

351

// Handle user input errors

352

if (error.type === LinearErrorType.InvalidInput) {

353

const fieldErrors = error.errors?.map(e => ({

354

field: e.path?.join('.'),

355

message: e.message

356

}));

357

throw new Error(`Invalid input: ${JSON.stringify(fieldErrors)}`);

358

}

359

}

360

361

// Re-throw non-Linear errors

362

throw error;

363

}

364

}

365

366

// Usage example

367

const issues = await handleLinearRequest(() =>

368

client.issues({ first: 50 })

369

);

370

```

371

372

**Rate Limit Handling:**

373

374

```typescript

375

class LinearClientWithRetry {

376

constructor(private client: LinearClient) {}

377

378

async requestWithRetry<T>(requestFn: () => Promise<T>, maxRetries = 3): Promise<T> {

379

for (let attempt = 1; attempt <= maxRetries; attempt++) {

380

try {

381

return await requestFn();

382

} catch (error) {

383

if (error instanceof RatelimitedLinearError && attempt < maxRetries) {

384

const delay = error.retryAfter || Math.pow(2, attempt); // Exponential backoff

385

console.log(`Rate limited, attempt ${attempt}/${maxRetries}, retrying in ${delay}s`);

386

await new Promise(resolve => setTimeout(resolve, delay * 1000));

387

continue;

388

}

389

throw error;

390

}

391

}

392

throw new Error(`Max retries (${maxRetries}) exceeded`);

393

}

394

}

395

```