Skip to content

Troubleshooting & FAQ

← docs home

"Node.js not found" / REQUEST_INVALID_HMAC (code 10005)

Every request needs the X-Hmac signature, computed by Node + ltsm.wasm.

  • Install Node.js 18+ and make sure node --version works in the same shell.
  • Non-standard path? set LINE_NODE=C:\path\to\node.exe (Windows) / export LINE_NODE=/path/to/node, or OkLine(config=LineConfig(node_path="...")).
  • For offline unit tests only, you can disable signing with OkLine(config=LineConfig(enable_hmac=False)) (requests will then be rejected by the real server — this is for mocked tests).

okline: command not found

The okline script is installed into your Python environment's Scripts/ (Windows) or bin/ (macOS/Linux) directory. If your shell can't find it:

  • Run the module form instead — it always works:
    python -m okline whoami
    
  • Or add the Scripts/bin directory to your PATH. On Windows it's usually ...\PythonXX\Scripts (or the venv's Scripts); pip show -f okline lists where the script landed.

[encrypted] messages / can't read Letter-Sealed text

If a received message shows up as [encrypted] (or its text is empty while it has a chunks field), your E2EE keys aren't loaded.

  • Log in once so the keychain is captured and saved:
    okline login
    
    This writes ./tokens.json including the E2EE keychain; later runs reuse it automatically.
  • In code, reuse that session and check readiness:
    api = OkLine.from_tokens_file("tokens.json")
    print(api.e2ee.is_ready())  # must be True to encrypt/decrypt
    msg = api.decrypt_message(received)  # returns plaintext `text`
    
  • If is_ready() is False, the keychain wasn't loaded — re-run okline login (scan QR → confirm PIN) to refresh it. E2EE keys load during qr_login and persist via save_tokens / from_tokens_file.
  • If only your own sent messages show [encrypted] while the other side decrypts fine, you are on a version before 2.7.1 — own-message decryption was fixed there; upgrade and re-run the command.

The phone shows "an error occurred" after scanning the QR

The QR must carry ?secret=<curve25519 pubkey>&e2eeVersion=1. OkLine adds this automatically in auth.qr_login — make sure you render the URL passed to your on_qr callback (not some other URL), and that you're on a current version.

The QR is unreadable in the terminal

  • Light-background terminal: print_qr(url, invert=True).
  • Windows console garbling the blocks: run chcp 65001 first, or use Windows Terminal / PowerShell 7.
  • Make it bigger: print_qr(url, style="full") (double width).
  • No inline QR at all? Install the optional extra: pip install "okline[qr]".

UnicodeEncodeError / garbled non-ASCII (Thai, emoji, …)

The CLI already forces UTF-8 output, so okline ... prints non-ASCII text correctly. In your own scripts, a legacy console encoding (e.g. Windows cp1252) can still raise UnicodeEncodeError. Fix it once at startup:

import sys

sys.stdout.reconfigure(encoding="utf-8")  # Python 3.7+

or set the environment variable before launching Python:

# Windows
set PYTHONUTF8=1
# macOS / Linux
export PYTHONUTF8=1

api.print_last() is already UTF-8 safe and degrades gracefully on a console that can't encode a character.

A response is None or a key is missing (KeyError)

The gateway wraps results as {"message":"OK","data":...}; OkLine unwraps .data. If you call the transport very directly you may see the envelope. Turn on raw logging to see exactly what came back:

LINE_DEBUG=1 python your_script.py

A non-OK envelope is raised as LineApiError (with .code, .reason).

getChats / getContactsInvalid Length (code 6)

The gateway rejects more than 100 mids in one getChats/getContactsV2 call. OkLine now auto-chunks these requests at 100 mids and merges the results, so you can pass arbitrarily long lists. If you still hit this, upgrade to the latest version (pip install -U okline).

401 / token expired

  • Pass a refresh_token so OkLine auto-refreshes: OkLine(access_token=..., refresh_token=...).
  • The gateway usually signals credential expiry with TalkException code 119 (MUST_REFRESH_V3_TOKEN), not HTTP 401. OkLine detects 119, renews the access token via the refresh hook (same path as 401) and replays the request once automatically — no caller action needed.
  • Or refresh manually: api.auth.refresh_access_token().
  • If you loaded the client with OkLine.from_tokens_file(...), the refreshed token is written back to the session file automatically.
  • If the session was revoked (logged out on another device), log in again (okline login).

LineAuthError on refresh — 10201 / 10202 (tokenRefresh codes)

The /api/auth/tokenRefresh endpoint answers with its own gateway envelope codes (the qU family), not TalkException codes:

  • 10201 AUTH_INVALID_REQUEST — a hard kickout: the refresh token is no longer valid (session revoked / logged out elsewhere). OkLine raises LineAuthError("token refresh rejected (AUTH_INVALID_REQUEST): re-login required"). Nothing recovers from this — log in again (okline login).
  • 10202 AUTH_RETRY_REQUIRED — the server is temporarily unwilling to refresh and asks the client to retry. refresh_access_token() retries with the server-provided refreshApiRetryPolicy (jittered exponential backoff, capped at maxDelayInMillis) — see Token refresh. If the retries are exhausted you get a LineAuthError; wait and refresh again, and consider OkLine(..., auto_refresh_schedule=True) so renewal happens proactively before the token goes stale.

LineMustUpgradeError / REQUEST_MUST_UPGRADE

The server wants a newer client version. The trigger is the outer envelope code 10006 (REQUEST_MUST_UPGRADE). The bundled app version is 3.7.2; if LINE forces an upgrade you may need a newer ltsm.wasm + version string from a fresh extension build (LineConfig(app_version=...), ltsm_origin=...).

Transient errors retried automatically (99999 / 115)

OkLine retries a request within the configured retry budget (LineConfig(max_retries=...), default 2) when the outer envelope code is 99999 (UNKNOWN_ERROR) or the nested TalkException code is 115 (SHOULD_RETRY) — matching the extension's axios retry condition — in addition to HTTP 5xx and network failures. If you still see these surface, the retries were exhausted; wait and retry your call.

Reading errors

from okline import LineApiError, enums

try:
    api.send_text(to, "hi")
except LineApiError as e:
    print(e.code, e.reason, e.metadata)
    # map a numeric code to a name:
    print(enums.ErrorCode(e.code).name if e.code is not None else "?")

Common ErrorCodes: AUTHENTICATION_FAILED(1), NOT_AVAILABLE_USER(7), NOT_AUTHORIZED_DEVICE(8), NOT_FRIEND(36), MUST_UPGRADE(50), EXPIRED_REVISION(52), MUST_REFRESH_V3_TOKEN(119). Codes 1/7/8 raise LineAuthError (re-login required; 119 is auto-refreshed first — see token expired). Full list in okline/enums.py.

Long-poll / SSE seems to hang

That's expected — iter_operations() and the verify long-polls block until something happens or the server times out. Use a thread, or set qr_login(wait_seconds=...).

Rate limits / abuse blocks

EXCESSIVE_ACCESS(4), ABUSE_BLOCK(35), CONGESTION_CONTROL(58) mean you're sending too fast or tripping anti-abuse. Slow down and only use your own account. You can pace requests automatically with the built-in token bucket:

from okline.ratelimit import RateLimiter

api.transport.rate_limiter = RateLimiter(rate=5, per=1.0)  # ~5 req/s

Still stuck?

Capture a redacted transcript and inspect it:

api.save_log("debug.txt")  # secrets masked by default
print(api.last.pretty())

See recording for the full transcript/HAR options.