# How to Handle Rate Limiting and Throttling in LINEJS

> Learn to handle rate limiting and throttling in LINEJS by catching InternalError exceptions and implementing exponential backoff retry logic for robust API interactions.

- Repository: [Evex  Developers/linejs](https://github.com/evex-dev/linejs)
- Tags: how-to-guide
- Published: 2026-03-01

---

**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`](https://github.com/evex-dev/linejs/blob/main/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`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/core/utils/error.ts).

### Error Codes Defined in the Thrift Schema

According to the Thrift definitions in [`packages/types/thrift.ts`](https://github.com/evex-dev/linejs/blob/main/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 of [`packages/linejs/base/request/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/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`](https://github.com/evex-dev/linejs/blob/main/packages/types/thrift.ts) around line 2900 and mirrored in [`packages/types/line_types.ts`](https://github.com/evex-dev/linejs/blob/main/packages/types/line_types.ts) around line 7479) while falling back to exponential delays:

```typescript
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

```typescript
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`](https://github.com/evex-dev/linejs/blob/main/example/square/pollingSquareChatEvents.ts), adding robust throttling handling to continuous polling loops:

```typescript
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 `limit` parameter (e.g., `fetchSquareChatEvents`, `fetchTalkMessages`). Lowering this value reduces the payload per request and decreases the likelihood of hitting per-method `CALLRATE` limits.
- **Serialize parallel requests** – If your application makes many different API calls simultaneously, serialize them or insert small delays between invocations to avoid triggering `EXCESSIVE_ACCESS` errors.
- **Enable client logging** – The `client.log` utility (used throughout the request pipeline in [`packages/linejs/base/request/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/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 `InternalError` with codes `CALLRATE`, `EXCESSIVE_ACCESS`, and `SERVER_BUSY` defined in [`packages/types/thrift.ts`](https://github.com/evex-dev/linejs/blob/main/packages/types/thrift.ts).
- The `RequestClient` does **not** automatically retry throttled requests; you must implement custom handling.
- Use exponential back-off with jitter, respecting the `retryTimeMillis` field when present in the error data.
- Wrap high-frequency operations like `fetchTalkMessages` or `fetchSquareChatEvents` in 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`](https://github.com/evex-dev/linejs/blob/main/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`](https://github.com/evex-dev/linejs/blob/main/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`](https://github.com/evex-dev/linejs/blob/main/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.