Errors
Every failure in @cstar.help/js throws a CStarError subclass. Catch by class.
Strings rotate; class identity doesn't. Every error carries a request ID; paste it into a support ticket
and we find the call in seconds.
Catch by class
Match on the typed subclass and recover differently per case. instanceof works
across every subpath import (/auth, /library, /community, /quickhelp, /proactive) because the class
identity is registered on a Symbol.for(...) slot of globalThis. Bundle
splits don't break the check.
import {
CStarError,
CStarValidationError,
CStarRateLimitError,
CStarAuthenticationError
} from '@cstar.help/js';
try {
await cstar.tickets.create(formData);
} catch (e) {
if (e instanceof CStarValidationError) {
// e.param holds the offending field name
return showFieldError(e.param, e.message);
}
if (e instanceof CStarRateLimitError) {
return retryAfter(e.retryAfter ?? 60); // seconds; undefined without Retry-After
}
if (e instanceof CStarAuthenticationError) {
// Bad/expired key. Bounce the user to settings.
return redirectToKeyRotation();
}
if (e instanceof CStarError) {
// Catch-all for any other typed cStar error.
log.error('cStar call failed', {
code: e.code,
requestId: e.requestId,
docUrl: e.docUrl,
statusCode: e.statusCode
});
}
throw e;
} Every error carries
The base class fields land on every subclass. Most of what you need to file a support ticket is already there.
The subclasses
Eight typed subclasses, dispatched by HTTP status and error type. A turned-off feature is the one exception to the status rule: it has its own class.
CStarAuthenticationError 401 Missing or invalid API key. Bad Authorization header.
CStarPermissionError 403 Wrong key type for the operation, a key for another team, your role lacks the permission, or the post's comments are closed. Not a turned-off feature: see the last class.
CStarNotFoundError 404 The resource ID doesn't exist (or your key can't see it).
CStarValidationError 400 / 422 Bad payload. e.param holds the field name. Also an Idempotency-Key reused with a different body (idempotency_key_reused, 422).
CStarConflictError 409 A unique-key conflict, or an Idempotency-Key still in use: its first request is
still running, or it was used on another endpoint. Also the team's webhook limit (webhook_limit_reached): waiting won't help, delete webhooks you no longer use first.
CStarRateLimitError 429 e.retryAfter tells you how many seconds to wait, or is undefined when the response had no Retry-After.
CStarServerError 5xx Our problem. Retry once with backoff; ping support with the request ID if it persists.
CStarFeatureDisabledError 403 The team turned off the feature this call needs (feature_disabled), such as
Community. e.feature names it and e.enableUrl links to the
setting. It is not a CStarPermissionError, so catch it by its own class.
Request IDs
Every error carries requestId. So does every successful response, via cstar.lastMeta.requestId. Both pull from the same source: the x-cstar-request-id header that every cStar response emits.
try {
const ticket = await cstar.tickets.create(payload);
log.info('ticket created', {
ticketId: ticket.id,
requestId: cstar.lastMeta.requestId
});
} catch (e) {
// Always log the request ID. Always.
log.error('ticket.create failed', {
code: e.code,
requestId: e.requestId,
docUrl: e.docUrl
});
throw e;
} Cross-subpath identity
The error classes live on globalThis[Symbol.for('@cstar.help/js/errors/v1')]. That
means an error thrown by LibraryClient (imported from @cstar.help/js/library) and caught with CStarError imported from the
root entry (different bundles, different module copies) still resolves to the same class
identity at runtime. instanceof works across every subpath.
Each subpath also re-exports the canonical error classes for ergonomics, so you can import { CStarError } from '@cstar.help/js/library' if you prefer.