Skip to content

Telegram inbound can fail with persistent session write lock on existing DM session #69790

Description

@r2sabiron

Bug: Telegram inbound / synthetic inbound can fail with persistent session write lock on existing DM session

Summary

On OpenClaw 2026.4.15, Telegram inbound processing can fail because the target session remains locked:

  • error: session file locked (timeout 10000ms)
  • lock owner shown as the live gateway process PID
  • affects both:
    • normal embedded agent processing for the Telegram DM session
    • synthetic Telegram update injection using the internal bot runtime path

This prevents a practical workaround where transcribed Telegram audio is converted into a synthetic inbound text message and fed back into the normal Telegram agent flow.

Environment

  • OpenClaw version: 2026.4.15
  • Host: Ubuntu 24.04
  • Channel: Telegram
  • Session scope: DM / per-channel-peer
  • Agent model: openai-codex/gpt-5.4
  • Gateway mode: local

Affected session

  • session key: agent:main:telegram:direct:8230009491
  • session id: 8b6fdd90-cb73-4261-ae35-70f687dea333
  • lock path:
    • /home/ubuntu/.openclaw/agents/main/sessions/8b6fdd90-cb73-4261-ae35-70f687dea333.jsonl.lock

What I was trying to do

The goal was to make Telegram audio messages transcribed locally with Whisper and then delivered into OpenClaw as actual user input to the existing Telegram conversation, instead of just echoing the transcript back to Telegram.

During investigation I confirmed:

  • Telegram inbound path reaches:
    • handleInboundMessageLike
    • processInboundMessage
    • debouncer / synthetic context helpers
    • processMessage(...)
  • bot-Ch7__EHu.js contains buildSyntheticContext(...) and uses synthetic Telegram messages internally in several places.

So I tested a synthetic inbound approach by constructing a Telegram update and calling bot.handleUpdate(update) against a locally created bot instance.

Expected behavior

A synthetic Telegram DM update for the existing user/session should be processed like a normal inbound Telegram message and produce a normal assistant turn.

Actual behavior

The processing reaches the embedded agent path, then fails with a session lock timeout:

  • Error: session file locked (timeout 10000ms): pid=6455 /home/ubuntu/.openclaw/agents/main/sessions/8b6fdd90-cb73-4261-ae35-70f687dea333.jsonl.lock

The PID belongs to the live openclaw-gateway process.

Reproduction notes

1. Existing Telegram DM session already exists

Normal Telegram DM session key:

  • agent:main:telegram:direct:8230009491

2. Create synthetic inbound update

Using internal runtime import from:

  • dist/extensions/telegram/bot-Ch7__EHu.js

Minimal script shape:

import fs from 'node:fs/promises';
import { t as createTelegramBot } from '/home/ubuntu/.openclaw/lib/node_modules/openclaw/dist/extensions/telegram/bot-Ch7__EHu.js';

const cfg = JSON.parse(await fs.readFile('/home/ubuntu/.openclaw/openclaw.json', 'utf8'));
const bot = createTelegramBot({ token: cfg.channels.telegram.botToken, config: cfg, accountId: 'default' });
await bot.init();

const chatId = 8230009491;
const now = Math.floor(Date.now() / 1000);

await bot.handleUpdate({
  update_id: 900000000 + now,
  message: {
    message_id: 800000000 + now,
    date: now,
    chat: { id: chatId, type: 'private', first_name: 'Ivan' },
    from: { id: chatId, is_bot: false, first_name: 'Ivan', username: 'Sabiron' },
    text: '[TEST] synthetic inbound'
  }
});

3. Observe failure

The run does not complete normally. Logs show embedded run failure due to session lock timeout.

Relevant logs

[diagnostic] lane task error: lane=main durationMs=15684 error="Error: session file locked (timeout 10000ms): pid=6455 /home/ubuntu/.openclaw/agents/main/sessions/8b6fdd90-cb73-4261-ae35-70f687dea333.jsonl.lock"
[diagnostic] lane task error: lane=session:agent:main:telegram:direct:8230009491 durationMs=15688 error="Error: session file locked (timeout 10000ms): pid=6455 /home/ubuntu/.openclaw/agents/main/sessions/8b6fdd90-cb73-4261-ae35-70f687dea333.jsonl.lock"
[model-fallback/decision] model fallback decision: decision=candidate_failed requested=openai-codex/gpt-5.4 candidate=openai-codex/gpt-5.4 reason=timeout next=none detail=session file locked (timeout 10000ms): pid=6455 /home/ubuntu/.openclaw/agents/main/sessions/8b6fdd90-cb73-4261-ae35-70f687dea333.jsonl.lock
Embedded agent failed before reply: session file locked (timeout 10000ms): pid=6455 /home/ubuntu/.openclaw/agents/main/sessions/8b6fdd90-cb73-4261-ae35-70f687dea333.jsonl.lock

Lock file contents while stuck:

{
  "pid": 6455,
  "createdAt": "2026-04-21T07:53:22.402Z",
  "starttime": 487253
}

And ps / lsof showed PID 6455 was the live openclaw-gateway process holding the lock file open.

Additional observations

  • This does not look like a simple stale lock file, because the owner PID is alive.
  • Startup stale-lock cleanup does not help because this lock is considered live.
  • The same logical session is impacted whether the inbound is normal or synthetic.
  • The session lock code appears to be in:
    • dist/session-write-lock-CcI4KSH8.js
  • Embedded run acquisition path appears in:
    • dist/pi-embedded-runner-DN0VbqlW.js
  • Cleanup should eventually release via embedded attempt cleanup, but in this case the lock appears to remain held long enough to break subsequent attempts.

Suspected issue

Possible lock release bug or long-lived reentrant hold in the embedded Telegram session flow, where a live gateway process retains the session write lock for an existing Telegram DM session longer than intended, causing subsequent runs to fail with session file locked.

Workaround currently used

As a fallback, local Whisper transcription was made to send transcript text back to Telegram with openclaw message send, but this is only a workaround because it echoes the transcript instead of feeding it into the agent as real inbound user input.

Why this matters

This blocks a useful integration pattern:

  • receive Telegram audio
  • transcribe locally
  • inject transcript into the same Telegram conversation as real user input

That pattern seems like it should be viable given the existing synthetic-message plumbing in the Telegram runtime.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions