Skip to content

Errors

Both SDKs ship the same exception hierarchy, with every class descending from TelerException. Some are raised by client-side validation (for example, an empty API key or an unsupported stream type); others are mapped from HTTP error responses. The two SDKs do not map every non-2xx status to a typed exception, so read the mapping table below carefully.

ExceptioncodeWhen raised
BadParametersException400Missing or invalid parameter. Carries .param.
UnauthorizedException401API key invalid, expired, or revoked.
ForbiddenException403Authenticated but not allowed.
NotImplementedException501Feature unsupported (e.g. unidirectional streams).
TelerException500Base class. Extend or use as a broad catch.

The SDKs differ in which HTTP statuses become typed exceptions:

SourceNodePython
HTTP 401UnauthorizedExceptionnot converted
HTTP 403ForbiddenExceptionForbiddenException
any other non-2xxTelerException (.code = HTTP status)not converted
empty API keyBadParametersExceptionBadParametersException
empty/unparseable remote_urlBadParametersExceptionBadParametersException
unidirectional streamNotImplementedExceptionNotImplementedException
bad stream-handler returnTelerExceptionBadParametersException

So BadParametersException and NotImplementedException are raised client-side, not returned by the server. The Node client wraps any non-401/403 HTTP error as a generic TelerException whose .code is the HTTP status; the Python client only auto-maps 403.

Exception classes live in teler.exceptions and are not re-exported from the top-level teler package, so import them from teler.exceptions. Use the broad TelerException as your last except clause to avoid leaking SDK internals.

from teler import Client
from teler.exceptions import (
BadParametersException,
ForbiddenException,
TelerException,
)
with Client("YOUR_API_KEY") as client:
try:
call = client.calls.create(
from_number="+918065xxxx",
to_number="+919967xxxx",
flow_url="https://your-domain.com/flow",
status_callback_url="https://your-domain.com/webhook",
)
except BadParametersException as e:
log.warning("Invalid %s: %s", e.param, e)
except ForbiddenException:
log.error("from_number not on this account")
raise
except TelerException as e:
log.exception("Teler error: %s", e)
raise

All Teler exceptions extend Error, so a generic catch (err) block sees them; use instanceof to discriminate. Each instance exposes message, code, and name.

import {
Client,
BadParametersException,
UnauthorizedException,
ForbiddenException,
TelerException,
} from "@frejun/teler";
const client = new Client("YOUR_API_KEY");
try {
const call = await client.calls.create({
from_number: "+918065xxxx",
to_number: "+919967xxxx",
flow_url: "https://your-domain.com/flow",
status_callback_url: "https://your-domain.com/webhook",
});
} catch (err) {
if (err instanceof BadParametersException) {
console.warn(`Invalid ${err.param}: ${err.message}`);
} else if (err instanceof UnauthorizedException) {
console.error("API key invalid");
throw err;
} else if (err instanceof ForbiddenException) {
console.error("from_number not on this account");
throw err;
} else if (err instanceof TelerException) {
console.error(`Teler error: ${err.message}`);
throw err;
} else {
throw err;
}
}

Every subclass accepts an optional message string. Use it when raising your own validation errors that should travel through the same channel.

from teler.exceptions import BadParametersException
raise BadParametersException(
param="from_number",
msg="from_number must be in E.164 format",
)

UnauthorizedException, ForbiddenException, and BadParametersException indicate caller errors: don’t retry. Fix the input.

TelerException (5xx) and transport-level failures are usually transient. Retry with exponential backoff and jitter, then circuit-break.

AttemptDelay
1immediate
21s + jitter
32s + jitter
44s + jitter

Stop after 3-5 attempts. Use a circuit breaker for sustained degradation.