Overview
The SDK raises errors through two related classes:
SdkError— thrown by SDK code itself (validation, configuration, deprecated paths, internal invariants). Always carries acode, astatus(HTTP-like), and an optionaloriginalError.AjaxError extends SdkError— thrown when an HTTP call to Bitrix24 fails. AddsrequestInfo(method,requestId, request params) so you can correlate with portal-side logs. Since v1.1.2 (#39),requestInfodoes not include the full request URL and credential-bearing fields insideparamsare redacted — the goal is to keep webhook secrets out oftoJSON()/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.
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.
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: 'tasks.task.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
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 helpersisMore,hasMore,getNext,fetchNextandgetTotalare deprecated and slated for removal in 2.0.0; useb24.actions.v{2,3}.callList.makeorfetchList.makeinstead.getNext()continues to throwJSSDK_CORE_METHOD_NOT_SUPPORT_IN_API_V3against v3 clients.
Choosing the method
Decision guide for picking between Call, CallList, FetchList, Batch and BatchByChunk in REST API v2 and v3.
Discovering v3 methods
Use rest.documentation.openapi to fetch the portal's own machine-readable list of every available REST API v3 method — the source of truth the SDK relies on instead of a hardcoded allowlist. Especially useful for AI agents and codegen.