# How to Handle Errors and Implement Retry Logic in LINEJS: A Complete Guide

> Master LINEJS error handling and retry logic. Discover how RequestClient automates token refresh and API error management, supporting custom exponential-backoff patterns for robust applications.

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

---

**LINEJS provides a layered error-handling strategy through the `RequestClient` class that automatically refreshes expired tokens, wraps API errors in `InternalError` objects, and supports custom exponential-backoff retry patterns via the `RetryPolicy` interface.**

LINEJS is a TypeScript client for the official LINE API that communicates via Thrift binary requests. Understanding how to handle errors and implement retry logic in LINEJS ensures your bot remains resilient against token expirations, network glitches, and transient API failures without manual intervention.

## Understanding the LINEJS Error Architecture

The library implements a four-layer error handling stack within the `RequestClient` class located in [`packages/linejs/base/request/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/request/mod.ts).

**Low-level Thrift parsing** occurs at lines 70-78, where binary responses convert to JavaScript objects and throw generic `Error` instances for malformed buffers.

**Typed API error translation** happens at lines 30-44, where LINE-specific structs like `TalkException` or `SquareException` transform into standardized `InternalError` objects that preserve the original error metadata.

**Automatic token refresh** triggers at lines 51-63 when the response contains `MUST_REFRESH_V3_TOKEN`, silently refreshing the access token and re-issuing the request once.

**E2EE decryption retry** operates in [`packages/linejs/base/e2ee/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/e2ee/mod.ts) (lines 79-100), attempting a second decryption with a fresh `Decipher` object if the first attempt fails.

## The InternalError Class

All API-specific errors unify under the `InternalError` class 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 class extends the native `Error` object and exposes three critical properties:

```typescript
export class InternalError extends Error {
  constructor(
    readonly type: string,
    override readonly message: string,
    readonly data: Record<string, LooseType> = {},
  ) {
    super(message);
    this.name = type;
    this.data = data;
  }
}

```

- **`type`** stores the exception name (e.g., `"TalkException"`, `"SquareException"`)
- **`message`** provides the human-readable description
- **`data`** contains the parsed Thrift struct including error codes and retry policies

### Handling Specific LINE API Errors

Use `instanceof` checks to distinguish API errors from network or parsing failures:

```typescript
import { InternalError } from "@evex/linejs/base/core/utils/error";

try {
  const profile = await client.talk.getProfile({ userMid: "Uxxxxxxxxxxxxxxxxxxxxxx" });
  console.log("Profile:", profile);
} catch (e) {
  if (e instanceof InternalError) {
    console.error(`LINE API error [${e.type}] – code: ${e.data.code}`, e.data);
    
    // Access server-sent retry policy if available
    if (e.data.retryPolicy) {
      const policy = e.data.retryPolicy;
      // Implement backoff using policy.initialDelayInMillis, etc.
    }
  } else {
    console.error("Unexpected error:", e);
  }
}

```

## Built-In Automatic Retry Mechanisms

### Token Refresh Flow

When `requestCore` detects `res.data.e?.code === "MUST_REFRESH_V3_TOKEN"` and a stored refresh token exists, the client:

1. Sets `isRefresh` to `true`
2. Calls `client.auth.tryRefreshToken()`
3. Re-issues the identical request with `isReRequest = true`
4. Returns the second attempt to your code transparently

You never need to handle token renewal manually; the `RequestClient` manages this entirely within [`packages/linejs/base/request/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/request/mod.ts).

### E2EE Decryption Retry

The end-to-end encryption module in [`packages/linejs/base/e2ee/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/e2ee/mod.ts) (lines 79-100) implements internal resilience. If `crypto.createDecipheriv` throws on the first attempt, the code creates a fresh `decipher2` instance and retries. Only if the second attempt fails does the error propagate to your application.

## Implementing Custom Retry Strategies

### Using the RetryPolicy Interface

The LINE API can return a `RetryPolicy` object defining exponential backoff parameters. This interface, generated from the Thrift schema in [`packages/types/line_types.ts`](https://github.com/evex-dev/linejs/blob/main/packages/types/line_types.ts), specifies:

```typescript
export interface RetryPolicy {
  initialDelayInMillis: Int64;
  maxDelayInMillis: Int64;
  multiplier: number;
  jitterRate: number;
}

```

Extract this policy from `InternalError.data` to implement server-directed backoff behavior.

### Custom Exponential Backoff Implementation

Apply the policy values to create a compliant retry loop:

```typescript
import type { RetryPolicy } from "@evex/linejs/types";

async function callWithPolicy<T>(fn: () => Promise<T>, policy: RetryPolicy) {
  let delay = Number(policy.initialDelayInMillis);
  const max = Number(policy.maxDelayInMillis);
  
  while (true) {
    try {
      return await fn();
    } catch (err) {
      if (delay > max) throw err;
      
      await new Promise(r => setTimeout(r, delay));
      delay = Math.min(delay * policy.multiplier, max);
      delay += delay * policy.jitterRate * Math.random();
    }
  }
}

```

### The User-Level Retry Helper

For simple use cases, copy the `retryApiCall` utility from [`example/square/pollingSquareChatEvents.ts`](https://github.com/evex-dev/linejs/blob/main/example/square/pollingSquareChatEvents.ts) (lines 43-57):

```typescript
async function retryApiCall<T>(fn: () => Promise<T>, retries = 4, delay = 3000): Promise<T> {
  let attempt = 0;
  while (true) {
    try {
      return await fn();
    } catch (e) {
      if (++attempt > retries) {
        throw new Error('retryApiCall: exhausted retries');
      }
      console.warn(`Retry ${attempt}/${retries} after ${delay}ms –`, e);
      await new Promise((r) => setTimeout(r, delay));
    }
  }
}

```

This helper catches any thrown error including `InternalError`, retries the async function up to `retries` times, and waits `delay` milliseconds between attempts.

## Practical Implementation Patterns

Combine built-in token refresh with user-level retry for maximum resilience against both authentication and transient network failures:

```typescript
import { LineClient } from "@evex/linejs";
import { retryApiCall } from "./retryHelper"; // copy from example/square/pollingSquareChatEvents.ts

const client = new LineClient({ /* auth config */ });

async function fetchSquareEvents() {
  // Token refresh happens automatically inside RequestClient
  // We add high-level retry for network glitches
  return await retryApiCall(() =>
    client.square.fetchMyEvents({ limit: 200 }),
    3,      // 3 attempts
    2000    // 2-second pause
  );
}

```

## Summary

- **LINEJS wraps all API errors** in the `InternalError` class defined in [`packages/linejs/base/core/utils/error.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/core/utils/error.ts), preserving type information and raw Thrift data.
- **Automatic token refresh** occurs in [`packages/linejs/base/request/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/request/mod.ts) (lines 51-63) when the API returns `MUST_REFRESH_V3_TOKEN`, requiring no manual intervention.
- **E2EE decryption failures** trigger an internal single retry in [`packages/linejs/base/e2ee/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/e2ee/mod.ts) before surfacing errors.
- **Server-directed retry policies** conform to the `RetryPolicy` interface in [`packages/types/line_types.ts`](https://github.com/evex-dev/linejs/blob/main/packages/types/line_types.ts), providing `initialDelayInMillis`, `multiplier`, and `jitterRate` values.
- **Generic retry utilities** like the `retryApiCall` helper in [`example/square/pollingSquareChatEvents.ts`](https://github.com/evex-dev/linejs/blob/main/example/square/pollingSquareChatEvents.ts) demonstrate how to layer additional resilience on top of the library's built-in mechanisms.

## Frequently Asked Questions

### How does LINEJS handle expired access tokens?

When the API returns the `MUST_REFRESH_V3_TOKEN` error code, the `RequestClient` class in [`packages/linejs/base/request/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/request/mod.ts) automatically calls `client.auth.tryRefreshToken()` and re-issues the failed request once. This happens transparently at lines 51-63, so your code receives the successful response without handling the refresh manually.

### Can I customize the retry behavior for specific API errors?

Yes. Catch `InternalError` instances and inspect `error.data.retryPolicy` to extract the server's recommended backoff parameters. Use these values—`initialDelayInMillis`, `maxDelayInMillis`, `multiplier`, and `jitterRate`—to implement custom exponential backoff logic, or adapt the `retryApiCall` helper from the examples to check error types before retrying.

### What is the difference between InternalError and standard JavaScript errors?

`InternalError` extends the native `Error` class and adds `type` and `data` properties. While standard errors indicate programming mistakes or network failures, `InternalError` specifically represents LINE API business logic failures (like permission denied or rate limiting) and contains the parsed Thrift error struct for programmatic handling.

### Where can I find a working example of retry logic in the LINEJS repository?

The file [`example/square/pollingSquareChatEvents.ts`](https://github.com/evex-dev/linejs/blob/main/example/square/pollingSquareChatEvents.ts) contains a production-ready `retryApiCall` function at lines 43-57. This utility demonstrates wrapping arbitrary API calls with a fixed-delay retry mechanism that you can extend to support exponential backoff or conditional retry based on error types.