SDKErrors & retries

Errors & retries

The SDK throws a small, typed hierarchy so you can handle failure precisely. Every error extends EdaError.

The hierarchy

ClassHTTPThrown whenTypical handling
EdaDeniedErrorYou used a guard/protect wrapper and the action was blocked or denied.Tell the model/user why (.reason).
EdaAuthError401API key missing, malformed, or revoked.Fix the key; don’t retry.
EdaQuotaError402Plan quota exceeded / payment required.Surface upgrade path; back off.
EdaForbiddenError403Key valid but not allowed for this resource.Check scopes/workspace.
EdaRateLimitError429Too many requests.Retried automatically with backoff; respect retryAfter.
EdaTimeoutErrorRequest exceeded timeoutMs.Retried (idempotent); then failMode applies.
EdaNetworkErrorDNS/socket/transport failure.Retried; then failMode applies.
EdaPendingTimeoutErrorcheckAndWait/waitForApproval timed out before a human decided.Return “still pending”; resume later via getDecision.
EdaConfigErrorMisconfiguration (e.g. no API key at all).Fix at startup; not retryable.
EdaApiError4xx/5xxAny other API error.Inspect .status / .code.
import {
  EdaDeniedError,
  EdaAuthError,
  EdaQuotaError,
  EdaRateLimitError,
  EdaPendingTimeoutError,
} from "@eda-holding-inc/sdk";
 
try {
  await refund({ amount: 900_00 });
} catch (err) {
  if (err instanceof EdaDeniedError) return replyToUser(err.reason);
  if (err instanceof EdaPendingTimeoutError) return replyToUser("Awaiting approval — I'll follow up.");
  if (err instanceof EdaAuthError) return alertOncall("Eda key rejected");
  throw err;
}

check() itself does not throw on a blocked/denied decision — it returns a Decision you branch on. The EdaDeniedError only comes from the guard/protect wrappers and adapters, which throw so a denied tool call short-circuits your agent loop.

Fail mode

When a check can’t complete (network, timeout, 5xx after retries), the client applies your failMode:

  • "closed" (default) — the decision resolves to not approved; the action does not run. Safe by default.
  • "open" — the decision resolves to approved; availability wins.
const eda = new Eda({ apiKey: process.env.EDA_API_KEY!, failMode: "closed" });

Retries

Idempotent calls (check, getDecision, listActions, usage, health) retry automatically on 429, timeouts, and transient 5xx/network errors, using exponential backoff with jitter. Configure or disable it:

const eda = new Eda({
  apiKey: process.env.EDA_API_KEY!,
  retry: {
    retries: 3,          // attempts after the first
    minDelayMs: 200,
    maxDelayMs: 3_000,
    factor: 2,           // exponential base
  },
});
 
// or turn it off entirely
const strict = new Eda({ apiKey: process.env.EDA_API_KEY!, retry: false });

EdaRateLimitError carries the server’s retryAfter (seconds) when present; the retrier honors it.

Observability

eda.stats() returns client-side counters (checks, approved, blocked, pending, errors, cacheHits) you can push to your metrics pipeline to watch how often agents are being gated — and how often Eda is in the hot path.