Errors
Catch FluxerError, read ErrorCodes, and turn API failures into useful replies.
User-facing validation and not-found cases in @fluxerjs/core throw FluxerError with an ErrorCodes value. Prefer checking the code over scraping the message string.
Catch and inspect
import { FluxerError, ErrorCodes } from '@fluxerjs/core';
try {
await guild.fetchMember(userId);
} catch (err) {
if (err instanceof FluxerError && err.code === ErrorCodes.MemberNotFound) {
await message.reply('That user is not in this server.');
return;
}
console.error(err);
await message.reply('Something went wrong.');
}FluxerError often wraps a REST failure in cause. HTTP status may live on err.statusCode or err.cause?.statusCode depending on the path.
Control automatic retries
REST requests retry up to three times by default. For non-idempotent or durably orchestrated mutations, use a request-aware policy to disable automatic retries while retaining them for reads:
import { Client } from '@fluxerjs/core';
const client = new Client({
rest: {
retries: 3,
retryPolicy: ({ method, defaultRetries }) =>
method === 'GET' ? defaultRetries : 0,
},
});The policy runs once per logical request and returns its retry budget. Return undefined to retain the configured default. It receives the request method, a sanitized routeKey for matching, and the default retry count. The exact caller-supplied route is not included as a separate field: Snowflake IDs are replaced by :id, webhook credentials are replaced by :token, query parameters are omitted, and absolute URLs are reduced to their origin plus /:external. Request bodies and headers are never exposed. Both configured and policy-selected retry counts must be non-negative safe integers.
Treat routeKey as matching metadata rather than a logging field when using custom relative routes, whose application-specific path segments cannot be classified by the SDK.
Disabling automatic retries does not make an ambiguous mutation safe to replay. A timeout or transport failure can occur after the server applied the request, so durable applications should persist an unknown outcome and reconcile it explicitly.
Common codes
Exact members live on ErrorCodes. Ones you will hit early:
| Situation | Typical code |
|---|---|
| Channel / message / member missing | *NotFound family |
| Bad fetch limit | INVALID_FETCH_LIMIT |
| Validation on a public method | named code for that check |
Builders (EmbedBuilder, etc.) may still throw plain RangeError for length limits. That is intentional.
Pattern for commands
Keep handlers boring: try the mutation, map known codes to a short reply, log everything else.
try {
await guild.ban(userId, { reason, deleteMessageDays: 1 });
await message.reply(`Banned <@${userId}>.`);
} catch (err) {
if (err instanceof FluxerError && err.code === ErrorCodes.MemberNotFound) {
await message.reply('User not found.');
return;
}
throw err; // or log + generic reply
}See moderation-bot for this pattern in a full bot.