Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.vibefollow.com/llms.txt

Use this file to discover all available pages before exploring further.

Every error thrown by @vibefollow/sdk extends VibeFollowError. Narrow to a subclass for typed recovery.
ClassHTTP statusRetryableMeaning
AuthError401 / 403NoAPI key missing, invalid, or revoked
ValidationError422NoRequest body failed validation; .field indicates
RateLimitError429Yes (auto)Rate limited; .retryAfterMs from Retry-After
ServerError5xxYes (auto)Server problem; retried with exponential backoff
NetworkError0Yes (auto)DNS, refused, abort, timeout
WebhookSignatureError0NoHMAC mismatch or timestamp outside tolerance
VibeFollowErrorother 4xxNoGeneric — base class for everything above

Worked example

import { AuthError, RateLimitError } from '@vibefollow/sdk';

try {
  await vf.users.signedUp('usr_42');
} catch (err) {
  if (err instanceof AuthError) {
    console.error('check your API key');
  } else if (err instanceof RateLimitError) {
    console.error('still rate limited after retries; backoff', err.retryAfterMs);
  } else {
    throw err;
  }
}
Auto-retries (with exponential backoff plus jitter) are applied on NetworkError, ServerError, and RateLimitError. Non-retryable errors throw on the first failure. The retry budget is maxRetries + 1 total attempts (default 3 total).

Error reference

Base class. Catch this to handle every SDK error uniformly.
class VibeFollowError extends Error {
  status: number;     // HTTP status or 0 for network/webhook
  code?: string;      // server-emitted error code, when available
}
import { VibeFollowError } from '@vibefollow/sdk';

try {
  await vf.users.signedUp('usr_42');
} catch (err) {
  if (err instanceof VibeFollowError) {
    logger.error({ status: err.status, code: err.code, message: err.message }, 'vibefollow error');
  }
  throw err;
}
The API key is missing, malformed, or revoked. Not retryable — this is a configuration problem on your side.
class AuthError extends VibeFollowError {
  status: 401 | 403;
  code?: string;
}
Recovery example
import { AuthError } from '@vibefollow/sdk';

try {
  await vf.users.signedUp('usr_42');
} catch (err) {
  if (err instanceof AuthError) {
    alerts.fire('vibefollow_auth', err.message);
  } else {
    throw err;
  }
}
Check the env var. If it’s set and looks right, the key has probably been revoked — issue a new one in the dashboard.
The request body failed validation. .field names the offending field when the server is able to pinpoint it.
class ValidationError extends VibeFollowError {
  status: 422;
  field?: string;   // e.g. "external_user_id", "properties.daysLeft"
  code?: string;
}
Recovery example
import { ValidationError } from '@vibefollow/sdk';

try {
  await vf.events.track('feature_used', 'usr_42', {});
} catch (err) {
  if (err instanceof ValidationError) {
    logger.error({ field: err.field, message: err.message }, 'vibefollow validation');
  } else {
    throw err;
  }
}
Fix the request shape. Common causes: required field missing, wrong type, value out of bounds.
You’ve hit a rate limit. .retryAfterMs carries the server’s Retry-After (parsed to milliseconds). The SDK retries automatically up to maxRetries; the error is only thrown if retries are exhausted.
class RateLimitError extends VibeFollowError {
  status: 429;
  retryAfterMs: number;
  code: 'rate_limited';
}
Recovery example
import { RateLimitError } from '@vibefollow/sdk';

try {
  await vf.users.signedUp('usr_42');
} catch (err) {
  if (err instanceof RateLimitError) {
    await sleep(err.retryAfterMs);
  } else {
    throw err;
  }
}
Wait retryAfterMs and try again, or batch your writes (vf.events.batch()).
Vibefollow had a problem. Retryable by definition; surfaced when retries are exhausted.
class ServerError extends VibeFollowError {
  status: 500 | 502 | 503 | 504 | …;
}
Recovery example
import { ServerError } from '@vibefollow/sdk';

try {
  await vf.users.signedUp('usr_42');
} catch (err) {
  if (err instanceof ServerError) {
    logger.warn({ status: err.status }, 'vibefollow transient error');
  } else {
    throw err;
  }
}
Check status.vibefollow.com if you’re seeing sustained ServerErrors. For single failures, the request can be safely retried later — the SDK’s Idempotency-Key prevents duplicates within a 24-hour window.
A network-layer failure: DNS, connection refused, timeout, aborted. Retryable.
class NetworkError extends VibeFollowError {
  status: 0;
  cause?: unknown;  // the underlying error
}
Recovery example
import { NetworkError } from '@vibefollow/sdk';

try {
  await vf.users.signedUp('usr_42');
} catch (err) {
  if (err instanceof NetworkError) {
    logger.warn({ cause: err.cause }, 'vibefollow network error');
  } else {
    throw err;
  }
}
Retry. The SDK does this automatically up to maxRetries; widen the budget if your environment is flaky.
HMAC mismatch, malformed X-Vibefollow-Signature header, or timestamp outside the ±5 minute window. Not retryable.
class WebhookSignatureError extends VibeFollowError {
  status: 0;
  code: 'webhook_signature_invalid';
}
Recovery example
import { WebhookSignatureError } from '@vibefollow/sdk';

try {
  const event = vf.webhooks.constructEvent(rawBody, sigHeader, secret);
  // ...
} catch (err) {
  if (err instanceof WebhookSignatureError) {
    return res.sendStatus(401);
  }
  throw err;
}
Return 401 to Vibefollow. Do not retry on your side; if the failure was transient, Vibefollow’s retry will pick it up. See Signature verification.