CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-linear--sdk

The Linear Client SDK for interacting with the Linear GraphQL API

Overview
Eval results
Files

error-handling.mddocs/

Error Handling

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

Capabilities

Error Types & Classification

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"
}

Specific Error Classes

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[]);
}

Error Parsing & Handling

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");
    }
  }
}

Error Response Types

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;
}

Best Practices

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

docs

comments-attachments.md

core-client.md

error-handling.md

index.md

issue-management.md

pagination-connections.md

project-management.md

team-user-management.md

webhook-processing.md

workflow-cycle-management.md

tile.json