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
```