Errors
Every error the SDK throws, by class and code: when it happens, what it means, and what to do. Includes the codes the UNET API passes through.
Every error the SDK throws extends UnetIdError, which has a code string. Branch on the class for the common cases, and on code for the rest. For patterns and examples, see Error handling.
import { AccessDenied, ApiError, InsufficientScope, NetworkError, RateLimited, SessionExpired, UnetIdError } from "@unet/id-web";
try {
await unet.api.feed();
} catch (e) {
if (e instanceof SessionExpired) {
// sign in again
} else if (e instanceof InsufficientScope) {
console.log("missing permission", e.scope);
} else if (e instanceof RateLimited) {
console.log("retry in", e.retryAfterSeconds, "seconds");
} else if (e instanceof ApiError) {
console.log(e.status, e.code);
} else if (e instanceof NetworkError || e instanceof AccessDenied || e instanceof UnetIdError) {
console.log(e.code);
}
}Classes
| Class | code | When | What to do |
|---|---|---|---|
AccessDenied | access_denied | The person pressed Deny on unet.social. Thrown by handleRedirectCallback(). | Return to your page and offer to try again. It is not an error of yours. |
StateMismatch | state_mismatch | The return URL has no matching pending sign-in: the state differs, there is none, or it is older than 15 minutes. | Start the sign-in again. See the FAQ. |
SessionExpired | session_expired | There is no session, the refresh was refused because the permission is gone, there is no refresh token and the access token ended, or the API answered 401. | Sign the person in again. The SDK has already cleared the session. |
InsufficientScope | insufficient_scope | The API answered 403 with insufficient_scope. It has a scope field naming the permission. Also thrown by a realtime stream. | Register the permission for your app, then sign in again with prompt: "consent". See Permissions. |
RateLimited | rate_limited | The API answered 429. It has retryAfterSeconds (or null). | Wait, then retry. Or set retries so the SDK does it. See Limits. |
NetworkError | network_error | No response arrived (offline, blocked, refused), a JSON request exceeded timeoutMs, or discovery or the signing keys could not be loaded. | Retry later. The session is kept. |
ApiError | See below | The API answered with an error that is not one of the above, or the SDK refused a call before sending it. It has status (HTTP). | Read code. 4xx is usually your request; 5xx is the server. |
UnetIdError | See below | Anything else from sign-in, configuration or the server-side checks. | Read code. |
UnetIdError codes
These come from sign-in, configuration and the verifyAccessToken and verifyIdToken checks. The class is the plain UnetIdError.
code | Thrown by | Meaning | What to do |
|---|---|---|---|
config | Constructor, verifyAccessToken, verifyIdToken | A required option is missing (clientId, redirectUri, clientSecret), the issuer is not https (http is allowed only on localhost), or Web Crypto is missing for verifyIdToken. | Fix the option. The message says which. |
discovery_invalid | signIn, restore, getAccessToken and anything that needs discovery | The discovery document names another issuer than the one you configured, or an endpoint is missing, is not https, or is on another origin. | Check issuer. If it is right, the server is misconfigured: its issuer must be its public https address. |
issuer_mismatch | handleRedirectCallback, verifyIdToken | The callback iss is not the configured issuer, or a discovery document names a different issuer than the one asked for. | Check issuer. If it keeps happening, treat it as a possible attack. |
invalid_client | Token requests, verifyAccessToken | UNET does not accept this App ID (wrong, turned off, on the other server), or, for verifyAccessToken, the App ID or Secret is wrong or the app has no secret. | Check the App ID, that the app is active, and that issuer points at the right server. |
invalid_response | handleRedirectCallback | The return URL has state but no code. | Start the sign-in again. |
token_error | Token requests | The token endpoint answered with an error and no more specific code. | Read the message. |
userinfo_error | handleRedirectCallback, restore, refreshUser, verifyAccessToken | The profile could not be loaded (the status is in the message). | Retry later. |
introspection_error | verifyAccessToken | UNET could not check the token (the status is in the message). | Treat the token as unverified. Retry later. |
jwks_error | verifyIdToken | The signing keys could not be loaded. | Retry later. |
The code can also be whatever the server named in an OpenID Connect error: the SDK passes it through. The ones you may meet:
code | When | What to do |
|---|---|---|
invalid_scope | The authorization asks for a permission the app did not register (a new app is held to what it registered). It arrives on the return URL. | Register the permission in the console, or stop asking for it. |
invalid_grant | A token request used a code that was already used or expired, or a refresh token that is gone. For a refresh, the SDK turns it into SessionExpired. | Start the sign-in again. |
invalid_request, server_error, temporarily_unavailable, login_required, consent_required, interaction_required | Standard OpenID Connect errors, from the return URL or the token endpoint. login_required, consent_required and interaction_required only appear when you pass prompt: "none". | Read the message. prompt: "none" never shows a screen, so fall back to a normal sign-in. |
ApiError codes
An ApiError has status and code. Some codes are made by the SDK; the rest come from the API.
Made by the SDK
code | status | When |
|---|---|---|
invalid_id | 400 | An id that goes into a request path is not a plain id (letters, digits, - and _ only), or the path has a . or .. segment. No request is sent. |
not_media_url | 400 | api.media() was given an address that is not a UNET media address, or whose path has anything but plain file-name segments. No request is sent. |
invalid_response | The status | A success answer that is not JSON. |
forbidden | 403 | A 403 without the API's insufficient_scope: usually a proxy or firewall in between. |
api_error | The status | An error answer with no code in it. |
stream_unavailable | The status | A realtime stream could not be opened. |
stream_garbled | 502 | A realtime stream sent a great deal of data without a break. |
Sent by the API
These are the codes the UNET API returns and the SDK passes on in code. Not every one applies to every call.
code | status | Meaning |
|---|---|---|
not_found | 404 | It does not exist, or you may not see it. The API never says which. |
not_allowed | 403 | A rule forbids it: blocked, private connections, comments off on that post, your own post, and the like. This is not a missing permission. |
connections_private | 403 | The person hides their followers and following. |
comments_disabled | 403 | Comments are off on that post. |
not_following | 403 | The action needs you to follow them first, such as the profile bell. |
edit_window_closed | 403 | A comment can be edited for 30 minutes after it is posted. |
fediverse_off, not_available, disclosure_required | 403, 403, 400 | The Fediverse is off for the account, is not available to it, or needs the plaintext disclosure accepted when turning it on. |
already_exists | 409 | The thing is already there. |
pin_limit_reached | 409 | The pinned-post limit is reached. The answer also carries limit. |
playlist_full | 409 | The profile music playlist is full. |
content_required, content_too_long, description_too_long, nothing_to_update | 400 | The body is empty, too long (comments: 2000 characters), or has nothing to change. |
invalid_request, invalid_field, invalid_query, invalid_cursor, invalid_id, invalid_username, invalid_feed_type, invalid_date, invalid_scheduled_at, invalid_coordinates, invalid_place, invalid_tag, invalid_topics, invalid_language, invalid_link, invalid_links, invalid_image_keys, invalid_gif_url, invalid_sticker_id, invalid_parent_id, invalid_reason, invalid_target_id, invalid_target_type, invalid_uri, invalid_file | 400 | A parameter or the body is not valid. The message says which. |
no_long_form | 404 | The post has no long-form text. |
parent_not_found | 404 | The comment you reply to is not on that post. |
not_found_or_published | 404 | There is no such scheduled post, or it is already published. |
payload_too_large | 413 | A picture is larger than 10 MB. |
gif_provider_error | 502, or the provider's own 4xx | The GIF provider failed. Try again. |
web_access_denied | 403 | The web version is not open to that account yet (only for UNET's own web app). |
open_api_unavailable, unet_id_starting | 503 | The API is restarting. Retry in a few seconds. |
internal_error | 500 | A server error. Retry later. |
A 401 from the API is always SessionExpired in the SDK, whatever the body says.