v1.2.0

Error codes and handling

Reference for SdkError and AjaxError codes raised by the SDK, plus the Bitrix24 REST error codes that surface through them.

Overview

The SDK raises errors through two related classes:

  • SdkError — thrown by SDK code itself (validation, configuration, deprecated paths, internal invariants). Always carries a code, a status (HTTP-like), and an optional originalError.
  • AjaxError extends SdkError — thrown when an HTTP call to Bitrix24 fails. Adds requestInfo (method, requestId, request params) so you can correlate with portal-side logs. Since v1.1.2 (#39), requestInfo does not include the full request URL and credential-bearing fields inside params are redacted — the goal is to keep webhook secrets out of toJSON() / toString() output.

Method-style results that don't throw — Call, CallList, Batch, BatchByChunk — surface failures through Result/AjaxResult: check .isSuccess and read .getErrorMessages(). FetchList, by contrast, does throw on failure (the generator can't complete partially).

import { SdkError, AjaxError } from '@bitrix24/b24jssdk'

try {
  // …SDK calls
}
catch (error) {
  if (error instanceof AjaxError) {
    // network / portal-side failure; error.code is the Bitrix24 code
    console.error(error.code, error.status, error.requestInfo?.method, error.requestInfo?.requestId)
  }
  else if (error instanceof SdkError) {
    // SDK-side issue; error.code starts with JSSDK_
    console.error(error.code, error.status)
  }
  else {
    throw error
  }
}

SdkError codes raised by the SDK

Codes are stable strings — match on them, don't parse messages.

CodeStatusWhere it's thrownWhat it means
JSSDK_CORE_B24_NOT_INIT500AbstractB24 gettersYou called a method on a B24 instance whose constructor never finished. For B24Frame, await initializeB24Frame(); for B24Hook, the URL was malformed.
JSSDK_CORE_B24_HTTP_V2_NOT_INIT500getHttpClient(ApiVersion.v2)The v2 HTTP client wasn't built (typically a configuration bug).
JSSDK_CORE_B24_HTTP_V3_NOT_INIT500getHttpClient(ApiVersion.v3)Same, for v3.
JSSDK_CORE_B24_API_WRONG500getHttpClientAn unknown ApiVersion was requested.
JSSDK_CORE_DEPRECATED_METHODlogger onlyNot thrown — emitted as a warning log when you call deprecated callMethod / callBatch / callListMethod / fetchListMethod / callBatchByChunk. Migrate to b24.actions.v2.* / v3.*.
JSSDK_CORE_METHOD_AVAILABLE_IN_API_V3logger onlyWarning emitted by CallV2.make() when the method also exists in v3. Migrate when you can.
JSSDK_CORE_METHOD_NOT_SUPPORT_IN_API_V3500CallV3.make(), BatchV3.make(), AjaxResult.getNext()You asked v3 to dispatch a method that's not in the v3 surface. Use v2, or split the batch by API version.
JSSDK_CORE_B24_FETCH_LIST_METHOD_API_V2500FetchListV2.make() generatorAn underlying page request failed; the generator stops. Catch around for await.
JSSDK_CORE_B24_FETCH_LIST_METHOD_API_V3500FetchListV3.make() generatorSame, for v3.
JSSDK_VERSION_MANAGER_NOT_DETECT_FOR_METHODversionManagerInternal — the method's API version couldn't be inferred. Usually paired with one of the codes above.
JSSDK_BATCH_TOO_LARGE400v2 HTTP clientMore than 50 commands handed to a single batch. Use BatchByChunk.
JSSDK_BATCH_EMPTY400v2 HTTP clientEmpty calls.
JSSDK_BATCH_SUB_ERROR500batch processingWrapper for a sub-call failure inside a batch. Inspect the per-call result.
JSSDK_INTERACTION_BATCH_BUILD_STRATEGY_V3_EMPTY_COMMAND400v3 batch processingA single command in a v3 batch was empty/malformed.
JSSDK_INTERACTION_BATCH_BUILD_STRATEGY_V3_EMPTY_COMMANDS400v3 batch processingThe whole commands array was empty.
JSSDK_INTERACTION_BATCH_STRATEGY_V2_EMPTY_COMMANDS400v2 batch processingSame, for v2.
JSSDK_INTERACTION_BATCH_STRATEGY_V2_EMPTY_COMMAND_RESPONSE500v2 batch processingA command in the response had no body — usually portal-side.
JSSDK_INTERACTION_BATCH_EMPTY_PROCESSING_STRATEGY500batch processingInternal — strategy lookup failed.
JSSDK_INTERACTION_BATCH_ROW_FAIL500batch row parserA single batch row could not be parsed.
JSSDK_INVALID_PARAMS400HTTP transportThe shape of params was rejected before the request was sent.
JSSDK_PARAMS_TOO_LARGE413HTTP transportSerialized request body exceeded the size limit. Split the call.
JSSDK_CALL_ALL_ATTEMPTS_EXHAUSTED500HTTP transportRetries (default 3, configurable via RestrictionParams.maxRetries) exhausted before the call succeeded.
JSSDK_UNKNOWN_ERROR500variousFallback when no more specific code applies.
JSSDK_INTERNAL_ERROR500SdkError.fromExceptionDefault code when wrapping an unknown exception.
JSSDK_INTERNAL_AJAX_ERROR500AjaxError.fromExceptionDefault code when wrapping an unknown HTTP error.
JSSDK_CLIENT_SIDE_WARNINGB24Hook constructorWarning emitted when a B24Hook is instantiated in the browser — webhooks expose a portal-wide secret and shouldn't ship to client bundles.

REST-side codes that come back as AjaxError

Bitrix24 returns these in the error field of an HTTP response. The SDK lifts them into AjaxError.code verbatim — match on the string.

CodeHTTP statusMeaningTypical fix
expired_token401OAuth access token expired.The SDK auto-refreshes for B24Frame and B24OAuth; for custom transports, refresh and retry once.
invalid_token401Token is malformed or revoked.User must re-auth (OAuth flow); webhook URL is wrong.
AUTHORIZE_ERROR403Caller is not allowed to perform this action.Wrong scope on the webhook/app, or the user lacks the access right (e.g. CRM permission).
WRONG_AUTH_TYPE403The token type doesn't match the endpoint (e.g. webhook used where OAuth is required).Use the correct authentication method for the endpoint.
QUERY_LIMIT_EXCEEDED503Per-second / per-method rate limit hit.Already handled by the built-in RateLimiter — increase restrictionParams.maxConcurrent only if you understand the cost.
OVERLOAD_LIMIT503Portal-wide load shedding.Back off, retry after a few seconds. The SDK's AdaptiveDelayer already does this.
ERROR_METHOD_NOT_FOUND400Method name is wrong, or the app doesn't have the scope to see it.Check spelling; check app.info's SCOPE against the method's required scope.
INVALID_REQUEST400Bad parameters (missing required field, wrong type).Inspect error.message — Bitrix24's description names the offending field.

This is not exhaustive — Bitrix24 publishes the full method-specific list at apidocs.bitrix24.com. Anything not listed here passes through as-is on AjaxError.code.

Handling patterns

Distinguish SDK bugs from portal-side failures

AjaxError extends SdkError, so order the instanceof checks specifically-first:

// @check-ignore: top-level try/catch with return, not valid at module scope

import { AjaxError, SdkError } from '@bitrix24/b24jssdk'

try {
  await $b24.actions.v2.call.make({ method: 'crm.deal.get', params: { id: 1 } })
}
catch (error) {
  if (error instanceof AjaxError) {
    // Portal-side: error.code is the Bitrix24 code (e.g. expired_token)
    if (error.code === 'expired_token') return refreshAndRetry()
    if (error.code === 'AUTHORIZE_ERROR') return showPermissionDenied()
    throw error
  }
  if (error instanceof SdkError) {
    // SDK-side: error.code starts with JSSDK_
    throw error // these are programmer errors, surface them
  }
  throw error
}

// @check-ignore: top-level try/catch with return, not valid at module scope

import { AjaxError, SdkError } from '@bitrix24/b24jssdk'

try {
  await $b24.actions.v3.call.make({ method: 'crm.deal.get', params: { id: 1 } })
}
catch (error) {
  if (error instanceof AjaxError) {
    // Portal-side: error.code is the Bitrix24 code (e.g. expired_token)
    if (error.code === 'expired_token') return refreshAndRetry()
    if (error.code === 'AUTHORIZE_ERROR') return showPermissionDenied()
    throw error
  }
  if (error instanceof SdkError) {
    // SDK-side: error.code starts with JSSDK_
    throw error // these are programmer errors, surface them
  }
  throw error
}

Use isSuccess for non-throwing methods

Call, CallList, Batch, BatchByChunk return a Result / AjaxResult instead of throwing:

// @check-ignore: top-level return in callList error-handling illustration

const response = await $b24.actions.v2.callList.make({ /* … */ })
if (!response.isSuccess) {
  console.error(response.getErrorMessages().join('; '))
  return
}
const items = response.getData()

// @check-ignore: top-level return in callList error-handling illustration

const response = await $b24.actions.v3.callList.make({ /* … */ })
if (!response.isSuccess) {
  console.error(response.getErrorMessages().join('; '))
  return
}
const items = response.getData()

For batches with isHaltOnError: false, isSuccess flips false on any sub-call failure but getData() still contains the successful entries. Iterate and check per-row .isSuccess if you need to know which calls passed.

Catch around for await for FetchList

// @check-ignore: top-level for-await in fetchList error-handling illustration

try {
  for await (const chunk of $b24.actions.v2.fetchList.make({ /* … */ })) {
    await persist(chunk)
  }
}
catch (error) {
  if (
    error instanceof SdkError
    && error.code === 'JSSDK_CORE_B24_FETCH_LIST_METHOD_API_V2'
  ) {
    // a page request failed; persisted chunks before this point are still good
    return resumeFromLastSavedId()
  }
  throw error
}

// @check-ignore: top-level for-await in fetchList error-handling illustration

try {
  for await (const chunk of $b24.actions.v3.fetchList.make({ /* … */ })) {
    await persist(chunk)
  }
}
catch (error) {
  if (
    error instanceof SdkError
    && error.code === 'JSSDK_CORE_B24_FETCH_LIST_METHOD_API_V3'
  ) {
    // a page request failed; persisted chunks before this point are still good
    return resumeFromLastSavedId()
  }
  throw error
}

Decide what to retry

Error conditionSafe to retry?
QUERY_LIMIT_EXCEEDED, OVERLOAD_LIMITYes — the built-in limiter already does.
expired_token (Frame/OAuth)Yes — already automatic. For custom transports: refresh, retry once.
invalid_token, AUTHORIZE_ERROR, WRONG_AUTH_TYPENo — needs human intervention (re-auth, fix scopes).
ERROR_METHOD_NOT_FOUND, INVALID_REQUESTNo — request is malformed; retrying gives the same error.
Network / 5xx without a specific codeYes — but only via JSSDK_CALL_ALL_ATTEMPTS_EXHAUSTED boundary; bump restrictionParams.maxRetries if you need more attempts.

See also

  • Choosing the right method — picks the right primitive before you have to read this page.
  • Restrictions System — rate limit, operating-time and adaptive delay configuration.
  • AjaxResult — full payload surface (isSuccess, getData, getErrorMessages). The v2-only paging helpers isMore, hasMore, getNext, fetchNext and getTotal are deprecated and slated for removal in 2.0.0; use b24.actions.v{2,3}.callList.make or fetchList.make instead. getNext() continues to throw JSSDK_CORE_METHOD_NOT_SUPPORT_IN_API_V3 against v3 clients.