How to Handle Rate Limiting and Throttling in LINEJS
LINEJS throws an InternalError with specific codes like CALLRATE, EXCESSIVE_ACCESS, or SERVER_BUSY when you hit rate limits, requiring you to catch these errors and implement exponential backoff retry logic in your application.
The LINEJS library (evex-dev/linejs) communicates with the LINE platform via the RequestClient located in packages/linejs/base/request/mod.ts. While the client automatically handles authentication token refreshes, it does not automatically retry requests that fail due to throttling. Instead, it propagates rate-limit errors as InternalError instances, giving you full control over back-off strategies and retry policies.
Understanding LINEJS Rate Limit Errors
When the LINE platform detects excessive request volume, it returns specific error codes in the Thrift response. The requestCore function parses these codes and throws an InternalError defined in packages/linejs/base/core/utils/error.ts.
Error Codes Defined in the Thrift Schema
According to the Thrift definitions in packages/types/thrift.ts, the primary throttling error codes are:
CALLRATE– Indicates you have exceeded the per-method call rate limit (defined around line 1094).EXCESSIVE_ACCESS– Signals that your client has exceeded the global quota for overall requests (defined around line 266).SERVER_BUSY– Returned when the LINE servers are temporarily overloaded (defined around line 358).
Each of these codes appears in the error.data.code field of the thrown InternalError.
Automatic Token Refresh vs. Manual Throttling Handling
The requestCore method distinguishes between recoverable authentication issues and throttling errors:
MUST_REFRESH_V3_TOKEN– The client automatically refreshes the authentication token and retries the request once (handled in lines 24–30 ofpackages/linejs/base/request/mod.ts).- Throttling errors (
CALLRATE,EXCESSIVE_ACCESS,SERVER_BUSY) – The client throws immediately without retrying, leaving handling to the consumer.
Implementing Exponential Backoff for LINEJS Throttling
To build resilient applications, wrap your API calls in a retry function that catches InternalError, inspects the error code, and applies exponential back-off with jitter.
Generic Retry Wrapper
This TypeScript implementation honors the optional retryTimeMillis field (defined in packages/types/thrift.ts around line 2900 and mirrored in packages/types/line_types.ts around line 7479) while falling back to exponential delays:
import { InternalError } from "@evex/linejs/base/core/utils/error.ts";
/**
* Retry wrapper for LINE API calls with exponential back-off.
*
* @param fn - The async LINE API call (e.g., client.talk.fetchTalkEvents)
* @param maxAttempts - Maximum retry attempts before giving up
* @param baseDelay - Initial delay in milliseconds
*/
async function retryWithBackoff<T>(
fn: () => Promise<T>,
maxAttempts = 5,
baseDelay = 1000,
): Promise<T> {
let attempt = 0;
while (true) {
try {
return await fn();
} catch (e) {
if (!(e instanceof InternalError)) throw e;
const code = e.data?.code;
if (!["CALLRATE", "EXCESSIVE_ACCESS", "SERVER_BUSY"].includes(code)) {
throw e;
}
attempt++;
if (attempt >= maxAttempts) {
throw new Error(
`Retry exhausted after ${maxAttempts} attempts: ${code}`,
);
}
// Honor server-suggested retry time if available
const suggestedDelay = Number(e.data?.retryTimeMillis);
const delay = Number.isFinite(suggestedDelay) && suggestedDelay > 0
? suggestedDelay
: baseDelay * 2 ** (attempt - 1) + Math.random() * 200;
console.warn(
`[LINEJS] Throttling (${code}) – retry #${attempt} in ${delay}ms`,
);
await new Promise((r) => setTimeout(r, delay));
}
}
}
Practical Usage Examples
Apply the retryWithBackoff wrapper to any LINEJS API method that might encounter rate limiting.
Fetching Talk Messages with Retry Logic
import { BaseClient } from "@evex/linejs/base";
import { FileStorage } from "@evex/linejs/storage";
async function fetchMessagesWithRetry(client: BaseClient) {
return await retryWithBackoff(() =>
client.talk.fetchTalkMessages({ limit: 30 }),
);
}
Polling Square Chat Events
This pattern mirrors the implementation found in example/square/pollingSquareChatEvents.ts, adding robust throttling handling to continuous polling loops:
import { BaseClient } from "@evex/linejs/base";
async function startPolling(client: BaseClient, chatMid: string) {
while (true) {
try {
const events = await retryWithBackoff(() =>
client.square.fetchSquareChatEvents({
squareChatMid: chatMid,
limit: 20,
direction: "FORWARD",
})
);
// Process events here
console.log("Received", events.length, "events");
} catch (e) {
console.error("Unrecoverable error:", e);
break;
}
// Pause between polls to reduce baseline load
await new Promise(r => setTimeout(r, 5_000));
}
}
Advanced Rate Limiting Considerations
Beyond basic retry logic, optimize your request patterns to minimize throttling occurrences:
- Reduce batch sizes – Many API methods accept a
limitparameter (e.g.,fetchSquareChatEvents,fetchTalkMessages). Lowering this value reduces the payload per request and decreases the likelihood of hitting per-methodCALLRATElimits. - Serialize parallel requests – If your application makes many different API calls simultaneously, serialize them or insert small delays between invocations to avoid triggering
EXCESSIVE_ACCESSerrors. - Enable client logging – The
client.logutility (used throughout the request pipeline inpackages/linejs/base/request/mod.ts) can be enabled to audit how often throttling occurs and adjust your back-off parameters accordingly.
Summary
- LINEJS surfaces rate limits via
InternalErrorwith codesCALLRATE,EXCESSIVE_ACCESS, andSERVER_BUSYdefined inpackages/types/thrift.ts. - The
RequestClientdoes not automatically retry throttled requests; you must implement custom handling. - Use exponential back-off with jitter, respecting the
retryTimeMillisfield when present in the error data. - Wrap high-frequency operations like
fetchTalkMessagesorfetchSquareChatEventsin a retry utility to ensure resilience.
Frequently Asked Questions
What error class does LINEJS throw when rate limiting occurs?
LINEJS throws InternalError (defined in packages/linejs/base/core/utils/error.ts). This error carries a data property containing the original LINE error code (e.g., CALLRATE) and optional fields like retryTimeMillis.
Does LINEJS automatically retry requests when the server is busy?
No. While LINEJS automatically handles MUST_REFRESH_V3_TOKEN by refreshing the authentication token and retrying once, it does not automatically retry requests that fail with CALLRATE, EXCESSIVE_ACCESS, or SERVER_BUSY. Your application code must catch these errors and implement the retry logic.
How can I find the suggested retry delay from the server?
Inspect error.data.retryTimeMillis on the caught InternalError. This field is defined in the Thrift schema (packages/types/thrift.ts line 2900) and represents the server-suggested wait time in milliseconds. If this value is missing or invalid, fall back to an exponential back-off calculation.
Where can I see a real-world example of throttling handling in the LINEJS repository?
The file example/square/pollingSquareChatEvents.ts demonstrates a practical implementation of a retryApiCall wrapper for polling Square chat events. This example shows how to structure continuous polling loops that gracefully handle transient rate limits without crashing the client.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →