Error codes

Complete BoardApiError catalog with guards, recovery decisions, retries, and observability.

Every non-2xx response throws BoardApiError. The client preserves the API’s machine code, human message, structured details, request ID, HTTP status, and parsed raw body.

If the response is not a parseable Cavuno error envelope, the SDK synthesizes unknown_error and retains the parsed body when one exists.

Handle an error once

Catch an error where the interface can make a recovery decision. Do not turn every error into an empty list.

Guards

GuardExact matchRecovery
isBoardApiErrorStructural BoardApiError brand, numeric status, and string codeLog structured fields and use a general failure boundary.
isNotFoundHTTP 404Render not found or try the managed redirect resolver.
isUnauthorizedHTTP 401Run the host-owned explicit refresh flow or sign out. The SDK does not refresh automatically.
isBoardPasswordRequired401 + board_password_requiredShow the board-password gate, not board-user login.
isForbiddenHTTP 403Explain or remove the action for the current viewer.
isConflictHTTP 409Re-read authoritative state before asking the user to resolve the conflict.
isRateLimitedHTTP 429Back off. Capture Retry-After in onResponse if the application needs the response header.
isValidationError400 + validation_bad_requestInterpret known details for field errors and keep an unknown-details fallback.

The guards are structural rather than instanceof-only so they remain correct when separate helper bundles contain their own class copy.

Complete known-code catalog

This is the complete exported BOARD_API_ERROR_CODES catalog in version 1.34.0. Codes can be added compatibly within API v1, so a default branch remains mandatory.

FamilyCodesRecovery and retry
Cross-cuttinginternal_error, validation_bad_request, validation_payload_too_large, rate_limited, rate_limit_unavailable, search_unavailable, too_many_filter_values, pagination_invalid_cursor, pagination_offset_too_large, auth_unauthenticated, auth_forbidden, plan_upgrade_required, unknown_errorCorrect validation, payload, filter, cursor, window, auth, or plan state. Back off public reads for rate/search/service failures; do not blindly retry writes or unknown outcomes.
Board resolution and pagesboards_not_found, board_page_not_foundCheck the board identifier or route. Do not retry unchanged input.
Board-user authenticationboard_auth_account_not_found, board_auth_email_taken, board_auth_invalid_credentials, board_auth_invalid_token, board_auth_registration_disabled, board_auth_token_expired, board_development_origin_not_allowed_for_email, board_development_origin_not_registered, board_development_origin_requires_publishable_key, invalid_current_password, no_password, same_email, email_taken, invalid_tokenRender the matching registration, sign-in, password, or email-change state. Credential failures are not transient. Passwordless accounts set a password with board.auth.forgotPassword; updatePassword returns no_password. email_taken on an email change is 400 and is distinct from registration’s 409 board_auth_email_taken. board_auth_account_not_found is 404 from requestMagicLink with intent: 'sign_in' when no account exists for that email. board_development_origin_not_registered is 400 when developmentOrigin names an origin the board has not registered; add it under Settings → SDK → Development origins. board_development_origin_requires_publishable_key is 400 when developmentOrigin is sent without a publishable-key (pk_...) board identifier. board_development_origin_not_allowed_for_email is 400 when register, forgotPassword, or requestMagicLink names a non-localhost development origin: email links can only target localhost, so test email sign-in there and use Google or LinkedIn sign-in on a preview origin.
Board-user sign-in methods and SSOboard_auth_method_unavailable, board_auth_sso_connection_not_found, board_auth_sso_role_unavailable, board_auth_sso_provider_unavailable, board_auth_sso_browser_mismatch, board_auth_sso_not_provisioned, board_auth_sso_identity_linked_elsewhereboard_auth_method_unavailable (403) means that sign-in method is switched off for the role: offer the built-in methods and SSO connections in details.availableMethods (isSignInMethodUnavailable) instead. Refresh board.context() after a connection or role error; retry board_auth_sso_provider_unavailable later. On board_auth_sso_browser_mismatch, ask the user to open the emailed link where they started signing in.
Board passwordboard_password_required, board_password_invalidRequest or re-request the board password grant. Do not treat this as a board-user 401.
Jobsjobs_not_found, jobs_invalid_filterReturn 404 for a missing job; correct or remove an invalid filter.
Taxonomycategories_not_found, skills_not_found, places_not_foundResolve a canonical slug or return 404.
Locationslocations_invalid_id, locations_unavailableAsk the user to pick the location again from board.locations.search, or retry later while the search is unavailable.
Companiescompanies_not_found, company_markets_not_found, company_salary_not_found, company_category_salary_not_foundReturn 404 or remove a link to data that no longer exists.
Salariessalaries_title_not_found, salaries_skill_not_found, salaries_location_not_found, salaries_title_location_not_found, salaries_skill_location_not_foundRedirect a resolved alias or return 404. Do not create an empty indexable salary page.
Blogblog_post_not_found, blog_author_not_found, blog_tag_not_foundHonor post redirects when returned; otherwise render 404.
Talenttalent_not_found, talent_directory_not_found, talent_directory_restrictedDistinguish a missing/private profile, disabled directory, and employer-only access. Restricted access can lead to employer sign-in or an upsell.
Job alertsjob_alert_not_found, job_alerts_disabled, candidate_alert_limit_reached, candidate_alert_not_foundRender missing/disabled state or ask the signed-in candidate to remove an alert before creating another. Retry only after state changes.
Candidate accountscandidate_entry_not_found, candidate_handle_taken, candidate_job_not_found, candidate_profile_required, resume_invalid_file, resume_upload_forbiddenReload a deleted entry, choose another handle, or correct file/permission state. candidate_profile_required means the signed-in account is not a candidate (for example an employer): show the employer profile view (avatar, displayName, link to employer screens) instead of candidate profile forms. Do not automatically repeat an upload.
Applicationsapplications_external_apply_only, applications_guest_not_allowed, applications_job_not_found, applications_native_disabled, applications_not_found, applications_resume_invalid_file, applications_unprocessableOpen the external apply URL, request sign-in, return 404, correct the file, or show the server’s workflow rejection. Native apply itself converges on an existing application; other writes are not blanket-retried.
Messagingmessaging_application_not_found, messaging_application_not_linked, messaging_blocked, messaging_cannot_block_self, messaging_cold_rule, messaging_conversation_not_found, messaging_disabled, messaging_edit_window_expired, messaging_message_deleted, messaging_message_not_found, messaging_not_author, messaging_not_participant, messaging_not_permitted, messaging_not_recipient, messaging_rate_limited, messaging_recipient_not_found, messaging_recipient_not_open, messaging_talent_access_required, messaging_unsend_window_expiredRefresh the thread and render the precise permission, lifecycle, or moderation outcome. Back off messaging_rate_limited; do not replay message writes automatically.
Employer self-serviceemployer_applicant_not_found, employer_ats_unprocessable, employer_checkout_failed, employer_company_exists, employer_company_name_taken, employer_company_not_found, employer_invite_not_found, employer_job_not_found, employer_job_slug_taken, employer_jobs_quota_exceeded, employer_member_not_found, employer_not_member, employer_payment_required, employer_pipeline_stage_not_found, not_company_admin, not_company_member, company_deletion_disabled, last_admin, already_member, already_invited, invalid_email, invite_email_mismatch, candidate_role, talent_list_not_found, sourced_candidate_not_found, sourced_convert_unavailableRe-read the membership, job, applicant, pipeline, quota, billing, talent-list, sourced, and invite state. Ask the employer to resolve naming, membership, payment, role, or workflow issues before retrying. sourced_convert_unavailable is 409 when the board has no native pipeline — do not map it to employer_company_name_taken.
Candidate paywallcandidate_paywall_access_required, paywall_already_active, paywall_disabled, paywall_invalid_checkout_session, paywall_no_candidate_profile, paywall_no_recurring_subscription, paywall_offer_not_foundRe-read offers and the access grant, create the required profile, or hide recurring management for lifetime access. Never infer access from a retry or redirect. candidate_paywall_access_required is 403 from recommended jobs and job alerts when the board's paywall is active and the candidate's job seeker plan does not unlock that feature; render the paywall offers in place of the feature rather than pre-gating from context flags.
Membershipsmembership_required403 on a posting attempt when the board requires an approved membership and the viewer's company holds none. Render a join gate that links to the board's membership plans (board.plans.list({ purpose: 'membership' })). Read board.context().posting.requiresMembership first so the gate replaces the form before anyone submits.
Employer talent-access paywalltalent_access_unavailable, company_required, already_on_plan, talent_access_required, messaging_talent_access_required, stripe_not_connectedHide or disable talent checkout when the board does not sell it; ask the viewer to pick a company when they have more than one membership; use in-place upgrade rather than a second checkout when already subscribed; spend unlocks/messages only with remaining credits. messaging_talent_access_required is also listed under Messaging.
Public job postingjob_posting_logo_lookup_unavailable, job_posting_logo_not_found, job_posting_rejectedContinue without automatic logo discovery when appropriate, or show the posting rejection. Do not automatically resubmit a paid posting.

Retry rules

The SDK performs exactly one fetch for every method call.

  • Public reads can be retried by the host with bounded backoff after 429 or transient 5xx/search failures.
  • A 401 refresh is explicit because refresh tokens rotate. Coordinate one refresh, persist the new pair, then retry the owning read once.
  • Do not retry validation, permission, not-found, plan, quota, or conflict errors until input or server state changes.
  • Do not blanket-retry writes. Some individual operations converge or support a specific retry contract, but the SDK provides no global mutation idempotency promise.
  • For a network failure after a write, the result can be unknown. Re-read state before offering another submission.

Observability and user copy

Use code for control flow and translation lookup. The server’s message is useful for diagnostics but is not a stable localization key. Treat details as unknown until narrowed for the specific code.

Log code, status, requestId, route or operation name, and non-sensitive context. Never log access tokens, password grants, resumes, application content, messages, checkout secrets, or the entire raw body by default.

Verify error handling

Request a known missing resource in development. Confirm that the UI reaches the 404 branch and the diagnostic event contains the server request ID when one was returned. Then simulate an unknown code and confirm the default branch remains reachable.