Skip to main content
The SDK throws typed errors for API failures, validation issues, and network problems. Catch and handle them for robust integrations.

Error classes

DevhelmError

The base error class for all API errors:
import { DevhelmError } from "@devhelm/sdk";

try {
  await client.monitors.get("nonexistent");
} catch (error) {
  if (error instanceof DevhelmError) {
    console.error(error.code);    // "NOT_FOUND"
    console.error(error.status);  // 404
    console.error(error.message); // "Monitor not found"
    console.error(error.detail);  // optional additional context
  }
}
PropertyTypeDescription
codeDevhelmErrorCodeError category
statusnumberHTTP status code
messagestringHuman-readable error message
detailstring | undefinedAdditional context

AuthError

Thrown for authentication and authorization failures (401, 403):
import { AuthError } from "@devhelm/sdk";

try {
  await client.monitors.list();
} catch (error) {
  if (error instanceof AuthError) {
    console.error("Check your API token");
  }
}
AuthError extends DevhelmError with code always set to "AUTH".

Error codes

CodeHTTP statusDescription
AUTH401, 403Invalid or expired token, insufficient permissions
NOT_FOUND404Resource does not exist
CONFLICT409Resource conflict (e.g., deploy lock held)
VALIDATION400, 422Invalid request body or parameters
API5xx, otherServer error or unexpected response

Handling patterns

Catch specific error types

import { DevhelmError, AuthError } from "@devhelm/sdk";

try {
  const monitor = await client.monitors.create({
    name: "API Health",
    type: "HTTP",
    config: { url: "https://api.example.com/health" },
    frequencySeconds: 60,
  });
} catch (error) {
  if (error instanceof AuthError) {
    // Re-authenticate or refresh token
  } else if (error instanceof DevhelmError) {
    switch (error.code) {
      case "VALIDATION":
        console.error("Invalid request:", error.detail);
        break;
      case "CONFLICT":
        console.error("Resource already exists");
        break;
      default:
        console.error(`API error: ${error.message}`);
    }
  } else {
    throw error; // Network error or unexpected issue
  }
}

Retry with backoff

async function withRetry<T>(fn: () => Promise<T>, maxRetries = 3): Promise<T> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (error instanceof DevhelmError && error.status === 429) {
        const delay = Math.pow(2, attempt) * 1000;
        await new Promise((r) => setTimeout(r, delay));
        continue;
      }
      throw error;
    }
  }
  throw new Error("Max retries exceeded");
}

const monitors = await withRetry(() => client.monitors.list());

Deploy lock contention

try {
  const lock = await client.deployLock.acquire({
    lockedBy: "ci-pipeline",
    ttlMinutes: 30,
  });
} catch (error) {
  if (error instanceof DevhelmError && error.code === "CONFLICT") {
    const current = await client.deployLock.current();
    console.error(`Lock held by ${current?.lockedBy}`);
  }
}

Next steps

Pagination

Iterate through paginated results.

Error patterns

Common error scenarios across all surfaces.