Linear Common Errors
Overview
Quick reference for diagnosing and resolving common Linear API errors.
Prerequisites
- Linear SDK or API access configured
- Access to application logs
- Understanding of HTTP status codes
Error Categories
Authentication Errors (401/403)
Invalid API Key
Error: Authentication required
Code: UNAUTHENTICATED
Causes:
- API key is invalid, expired, or revoked
- Key format is incorrect (should start with
lin_api_) - Environment variable not loaded
Solutions:
// Verify key format
const apiKey = process.env.LINEAR_API_KEY;
if (!apiKey?.startsWith("lin_api_")) {
console.error("Invalid API key format");
}
// Test authentication
const client = new LinearClient({ apiKey });
try {
await client.viewer;
console.log("Authentication successful");
} catch (e) {
console.error("Authentication failed:", e);
}
Permission Denied
Error: You don't have permission to access this resource
Code: FORBIDDEN
Causes:
- API key doesn't have required scope
- User not a member of the team/organization
- Resource belongs to different workspace
Solutions:
- Check API key permissions in Linear Settings > API
- Verify team membership
- Regenerate key with correct permissions
Rate Limiting Errors (429)
Error: Rate limit exceeded
Code: RATE_LIMITED
Headers: X-RateLimit-Remaining: 0, Retry-After: 60
Causes:
- Too many requests in time window
- Burst of requests without throttling
Solutions:
// Implement exponential backoff
async function withRetry<T>(
fn: () => Promise<T>,
maxRetries = 3
): Promise<T> {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error: any) {
if (error?.extensions?.code === "RATE_LIMITED" && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000;
console.log(`Rate limited, retrying in ${delay}ms...`);
await new Promise(r => setTimeout(r, delay));
continue;
}
throw error;
}
}
throw new Error("Max retries exceeded");
}
Validation Errors (400)
Error: Variable "$input" got invalid value
Code: BAD_USER_INPUT
Common Validation Failures:
| Field | Error | Fix |
|---|---|---|
teamId |
"Team not found" | Verify team exists and accessible |
priority |
"Invalid priority" | Use 0-4 (0=None, 1=Urgent, 4=Low) |
estimate |
"Invalid estimate" | Use team's configured values |
stateId |
"State not found" | State must belong to same team |
assigneeId |
"User not found" | User must be team member |
Debug Validation:
async function createIssueWithValidation(input: {
teamId: string;
title: string;
stateId?: string;
assigneeId?: string;
}) {
// Validate team exists
const team = await client.team(input.teamId);
if (!team) throw new Error(`Team not found: ${input.teamId}`);
// Validate state belongs to team
if (input.stateId) {
const states = await team.states();
if (!states.nodes.find(s => s.id === input.stateId)) {
throw new Error(`State ${input.stateId} not in team ${team.key}`);
}
}
// Validate assignee is team member
if (input.assigneeId) {
const members = await team.members();
if (!members.nodes.find(m => m.id === input.assigneeId)) {
throw new Error(`User ${input.assigneeId} not in team ${team.key}`);
}
}
return client.createIssue(input);
}
GraphQL Errors
Error: Cannot query field "nonExistent" on type "Issue"
Code: GRAPHQL_VALIDATION_FAILED
Causes:
- Field name typo
- Querying deprecated field
- SDK version mismatch with API
Solutions:
# Update SDK to latest
npm update @linear/sdk
# Check API schema
curl -H "Authorization: $LINEAR_API_KEY" \
https://api.linear.app/graphql \
-d '{"query": "{ __schema { types { name } } }"}'
Network Errors
Error: fetch failed
Cause: ECONNREFUSED / ETIMEDOUT
Solutions:
// Add timeout and retry
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 30000);
try {
const response = await fetch("https://api.linear.app/graphql", {
signal: controller.signal,
// ... other options
});
} finally {
clearTimeout(timeout);
}
Diagnostic Commands
Test API Connection
curl -s -H "Authorization: $LINEAR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "{ viewer { name email } }"}' \
https://api.linear.app/graphql | jq
Check Rate Limit Status
curl -I -H "Authorization: $LINEAR_API_KEY" \
https://api.linear.app/graphql
# Look for: X-RateLimit-Limit, X-RateLimit-Remaining
Validate Webhook Signature
import crypto from "crypto";
function verifyWebhookSignature(
body: string,
signature: string,
secret: string
): boolean {
const hmac = crypto.createHmac("sha256", secret);
const digest = hmac.update(body).digest("hex");
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(digest)
);
}
Error Handling Pattern
import { LinearClient, LinearError } from "@linear/sdk";
async function safeLinearCall<T>(
operation: () => Promise<T>,
context: string
): Promise<T> {
try {
return await operation();
} catch (error) {
if (error instanceof LinearError) {
console.error(`Linear API Error in ${context}:`, {
message: error.message,
type: error.type,
errors: error.errors,
});
// Handle specific error types
switch (error.type) {
case "AuthenticationError":
throw new Error("Please check your Linear API key");
case "RateLimitedError":
throw new Error("Too many requests, please retry later");
default:
throw error;
}
}
throw error;
}
}
Resources
Next Steps
Set up comprehensive debugging with linear-debug-bundle.