The Linear Client SDK for interacting with the Linear GraphQL API
Comprehensive error handling system with Linear-specific error types, rate limiting support, and structured error information for robust API integration.
Linear SDK provides a hierarchy of error types for different failure scenarios.
/**
* Base error class for all Linear API errors
*/
class LinearError extends Error {
/** The type of the first error returned by the Linear API */
type?: LinearErrorType;
/** A list of GraphQL errors returned by the Linear API */
errors?: LinearGraphQLError[];
/** The GraphQL query that caused this error */
query?: string;
/** The GraphQL variables that caused this error */
variables?: Record<string, unknown>;
/** Any data returned by this request */
data?: unknown;
/** The HTTP status of this request */
status?: number;
/** The raw LinearGraphQLClient error */
raw?: LinearErrorRaw;
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[], type?: LinearErrorType);
}
/**
* Individual GraphQL error representation
*/
class LinearGraphQLError {
/** The type of this GraphQL error */
type: LinearErrorType;
/** A friendly error message */
message: string;
/** If this error is caused by the user input */
userError?: boolean;
/** The path to the GraphQL node at which the error occurred */
path?: string[];
constructor(error?: LinearGraphQLErrorRaw);
}
/**
* Enumeration of all error types returned by the Linear API
*/
enum LinearErrorType {
FeatureNotAccessible = "FeatureNotAccessible",
InvalidInput = "InvalidInput",
Ratelimited = "Ratelimited",
NetworkError = "NetworkError",
AuthenticationError = "AuthenticationError",
Forbidden = "Forbidden",
BootstrapError = "BootstrapError",
Unknown = "Unknown",
InternalError = "InternalError",
Other = "Other",
UserError = "UserError",
GraphqlError = "GraphqlError",
LockTimeout = "LockTimeout",
UsageLimitExceeded = "UsageLimitExceeded"
}Each error type has a dedicated class with specific properties and behavior.
/**
* Authentication-related errors
*/
class AuthenticationLinearError extends LinearError {
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);
}
/**
* Permission-related errors
*/
class ForbiddenLinearError extends LinearError {
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);
}
/**
* Input validation errors
*/
class InvalidInputLinearError extends LinearError {
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);
}
/**
* Rate limiting errors with detailed rate limit information
*/
class RatelimitedLinearError extends LinearError {
/** How long, in seconds, to wait before making a follow-up request */
retryAfter: number | undefined;
/** The max amount of requests allowed in the duration */
requestsLimit: number | undefined;
/** The remaining requests before rate limiting */
requestsRemaining: number | undefined;
/** Unix timestamp at which the requests will be reset */
requestsResetAt: number | undefined;
/** The max amount of complexity allowed in the duration */
complexityLimit: number | undefined;
/** The remaining complexity before rate limiting */
complexityRemaining: number | undefined;
/** Unix timestamp at which the complexity will be reset */
complexityResetAt: number | undefined;
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);
}
/**
* Network connectivity errors
*/
class NetworkLinearError extends LinearError {
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);
}
/**
* Feature access restrictions
*/
class FeatureNotAccessibleLinearError extends LinearError {
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);
}
/**
* Internal server errors
*/
class InternalLinearError extends LinearError {
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);
}
/**
* Usage limit exceeded errors
*/
class UsageLimitExceededLinearError extends LinearError {
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);
}
/**
* Database lock timeout errors
*/
class LockTimeoutLinearError extends LinearError {
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);
}
/**
* Bootstrap initialization errors
*/
class BootstrapLinearError extends LinearError {
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);
}
/**
* Other unclassified errors
*/
class OtherLinearError extends LinearError {
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);
}
/**
* User-related errors
*/
class UserLinearError extends LinearError {
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);
}
/**
* GraphQL-specific errors
*/
class GraphqlLinearError extends LinearError {
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);
}
/**
* Unknown or unclassified errors
*/
class UnknownLinearError extends LinearError {
constructor(error?: LinearErrorRaw, errors?: LinearGraphQLError[]);
}Utility functions for parsing and handling errors from the Linear API.
/**
* Parse raw errors from Linear API into typed error instances
* @param error - Raw error from LinearGraphQLClient or existing LinearError
* @returns Properly typed LinearError instance
*/
function parseLinearError(error?: LinearErrorRaw | LinearError): LinearError;Usage Examples:
import { LinearClient, LinearError, LinearErrorType, RatelimitedLinearError } from "@linear/sdk";
const client = new LinearClient({ apiKey: "your-api-key" });
try {
const issues = await client.issues({ first: 100 });
} catch (error) {
if (error instanceof LinearError) {
console.log("Error type:", error.type);
console.log("Error message:", error.message);
console.log("HTTP status:", error.status);
console.log("GraphQL query:", error.query);
// Handle specific error types
switch (error.type) {
case LinearErrorType.AuthenticationError:
console.log("Authentication failed - check your API key");
break;
case LinearErrorType.Ratelimited:
if (error instanceof RatelimitedLinearError) {
console.log("Rate limited - retry after:", error.retryAfter);
console.log("Requests remaining:", error.requestsRemaining);
console.log("Rate limit resets at:", new Date(error.requestsResetAt! * 1000));
}
break;
case LinearErrorType.Forbidden:
console.log("Access forbidden - insufficient permissions");
break;
case LinearErrorType.InvalidInput:
console.log("Invalid input provided");
if (error.errors) {
error.errors.forEach(graphqlError => {
console.log("Field error:", graphqlError.path, graphqlError.message);
});
}
break;
default:
console.log("Unexpected error occurred");
}
}
}Type definitions for raw error responses and contexts.
/**
* Raw error returned by the Linear API
*/
interface LinearErrorRaw {
/** Error name if available */
name?: string;
/** Error message if available */
message?: string;
/** Error information for the request */
request?: GraphQLRequestContext<Record<string, unknown>>;
/** Error information for the response */
response?: LinearRawResponse<unknown>;
}
/**
* One of potentially many raw GraphQL errors returned by the Linear API
*/
interface LinearGraphQLErrorRaw {
/** The error type */
message?: LinearErrorType;
/** The path to the GraphQL node at which the error occurred */
path?: string[];
extensions?: {
/** The error type */
type?: LinearErrorType;
/** If caused by the user input */
userError?: boolean;
/** A friendly error message */
userPresentableMessage?: string;
};
}
/**
* The raw response from the Linear GraphQL Client
*/
interface LinearRawResponse<Data> {
/** The returned data */
data?: Data;
/** Any extensions returned by the Linear API */
extensions?: unknown;
/** Response headers */
headers?: Headers;
/** Response status */
status?: number;
/** An error message */
error?: string;
/** Any GraphQL errors returned by the Linear API */
errors?: LinearGraphQLErrorRaw[];
}
/**
* Description of a GraphQL request used in error handling
*/
interface GraphQLRequestContext<Variables extends Record<string, unknown>> {
query: string;
variables?: Variables;
}Error Handling Strategy:
import { LinearClient, LinearError, LinearErrorType } from "@linear/sdk";
async function handleLinearRequest<T>(requestFn: () => Promise<T>): Promise<T | null> {
try {
return await requestFn();
} catch (error) {
if (error instanceof LinearError) {
// Log structured error information
console.error("Linear API Error:", {
type: error.type,
message: error.message,
status: error.status,
query: error.query?.substring(0, 100), // Truncate for logging
timestamp: new Date().toISOString()
});
// Handle retryable errors
if (error.type === LinearErrorType.Ratelimited && error instanceof RatelimitedLinearError) {
if (error.retryAfter && error.retryAfter < 60) {
console.log(`Rate limited, retrying in ${error.retryAfter} seconds`);
await new Promise(resolve => setTimeout(resolve, error.retryAfter! * 1000));
return handleLinearRequest(requestFn); // Retry once
}
}
// Handle authentication errors
if (error.type === LinearErrorType.AuthenticationError) {
throw new Error("Linear authentication failed - please check your API credentials");
}
// Handle user input errors
if (error.type === LinearErrorType.InvalidInput) {
const fieldErrors = error.errors?.map(e => ({
field: e.path?.join('.'),
message: e.message
}));
throw new Error(`Invalid input: ${JSON.stringify(fieldErrors)}`);
}
}
// Re-throw non-Linear errors
throw error;
}
}
// Usage example
const issues = await handleLinearRequest(() =>
client.issues({ first: 50 })
);Rate Limit Handling:
class LinearClientWithRetry {
constructor(private client: LinearClient) {}
async requestWithRetry<T>(requestFn: () => Promise<T>, maxRetries = 3): Promise<T> {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await requestFn();
} catch (error) {
if (error instanceof RatelimitedLinearError && attempt < maxRetries) {
const delay = error.retryAfter || Math.pow(2, attempt); // Exponential backoff
console.log(`Rate limited, attempt ${attempt}/${maxRetries}, retrying in ${delay}s`);
await new Promise(resolve => setTimeout(resolve, delay * 1000));
continue;
}
throw error;
}
}
throw new Error(`Max retries (${maxRetries}) exceeded`);
}
}Install with Tessl CLI
npx tessl i tessl/npm-linear--sdk