Error handling
API and network failures reject with JanuaryError. Branch on category, and use code for detail:
import { JanuaryError } from '@januaryai/web-sdk';
const controller = new AbortController();
try {
await user.foods.search({ query: 'banana', signal: controller.signal });
} catch (error) {
if (controller.signal.aborted) {
// Your code canceled the request; not a failure.
} else if (error instanceof JanuaryError) {
switch (error.category) {
case 'validation': /* show error.message next to the input */ break;
case 'notFound': /* fall back */ break;
case 'rateLimit': /* back off */ break;
case 'authentication':
case 'authorization': /* check the session and the token's scopes */ break;
default: /* show a generic error; log error.requestId */
}
} else {
throw error;
}
}Categories
Category
HTTP status
Common code values
What to do
validation
400, 422
invalid_request, date_range_too_large, daily_water_limit_exceeded, image_unreachable, image_corrupt, image_format_unsupported, image_invalid_base64
Fix the input. Sending the same request again fails the same way.
authentication
401, or none
unauthorized, token_invalid, token_revoked. With no status: the token provider ran out of retries or returned an unusable token.
Check your token endpoint and the app session. The SDK already handles token_expired.
authorization
403
scope_insufficient, end_user_id_mismatch, forbidden
Mint the token with the scopes the call needs (scopes).
notFound
404
not_found
Fall back, for example to text search after a barcode miss.
rateLimit
429
rate_limited, request_limit_exceeded, credit_limit_exceeded
Back off on rate_limited. The other two last until your billing period resets (Credits).
server
500–599
internal_error, upstream_error, service_unavailable, upstream_timeout
Retry reads with backoff. Before retrying a create, list the day to check it didn't go through.
transport
none
none
The request didn't complete or never started: a network failure, an origin January hasn't enabled, a failing token provider, or invalid log input. Check cause.
unknown
Any other, including 409 and 413
conflict, payload_too_large
Show a generic error and log requestId.
status, code, and requestId are set when January answered. All codes are listed in REST errors. Log category, status, code, and requestId; never log tokens, meal images, nutrition data, or health profiles.
Invalid input
Most invalid requests throw a TypeError before anything is sent: food, restaurant, and food-analysis requests, foodLogs.update with no fields or a bad timestampUtc, and forUser with a blank end-user ID. FoodPortion.from throws FoodPortionError, and preparePhotoScanImage throws TypeError or RangeError.
In 0.3.1, invalid input to the other food-, water-, and weight-log calls and to glucose timestamps is also caught before anything is sent, but it rejects as a JanuaryError with category transport whose cause is the TypeError. Examples are an impossible date such as 2026-02-31, an unknown unit, and a timestamp that isn't a date.
Cancellation
A request canceled through its signal rejects with an AbortError (error.name === 'AbortError'), not with a JanuaryError. Check your signal's aborted flag first, as the sample does, so cancellation isn't handled as a failure. An AbortError thrown by your token provider also reaches the caller unchanged.
Last updated
Was this helpful?