Nextcloud Talk: LLM Processes Messages But Sends No Reply Back
Regression in OpenClaw 2026.5.4+ causing nextcloud-talk adapter to receive messages and trigger LLM processing but fail to send responses, with gateway logs showing 'incomplete turn detected: payloads=0'.
π Symptoms
Primary Manifestation: The bot receives webhook messages from Nextcloud Talk, triggers LLM processing (GPU load confirms activity), but no response is ever delivered back to the Nextcloud Talk room.
Gateway Log Evidence:
[gateway] incomplete turn detected: payloads=0 β surfacing error to user
[gateway] turn timeout reached, surfacing partial response
[nextcloud-talk] WARN: no response payload generated for turn 1729a3b1
Session Logging Anomaly: bash
commands.log shows Telegram sessions but NO nextcloud-talk sessions
$ grep “nextcloud-talk.*session” /var/log/openclaw/commands.log
(empty result β only telegram sessions present)
$ grep “telegram.*session” /var/log/openclaw/commands.log 2026-05-10 14:23:41 [telegram] session started: user_id=12345678 adapter=telegram 2026-05-10 14:23:45 [telegram] session ended: turn_count=3 duration=4.2s
Webhook Receipt Confirmation: bash
Port 8788 confirms listening on all interfaces
$ ss -tlnp | grep 8788 LISTEN 0 128 0.0.0.0:8788 0.0.0.0:* users:((“node”,pid=12345,fd=20))
Nextcloud API connectivity test (HTTP 401 = auth required, connection works)
$ curl -v https://nc.example.com/ocs/v2.php/apps/spreed/api/v4/room HTTP/2 401 www-authenticate: Basic realm=“Nextcloud”
Affected Configuration:
- Standard rooms configuration with
requireMention: false groupPolicy: openenabled- Nextcloud 33.x with Talk bot installed via
occ talk:bot:install
Version Scope:
- OpenClaw 2026.5.4, 2026.5.5, 2026.5.6 all affected
@openclaw/nextcloud-talktested across same versions- Telegram adapter on identical setup continues working
π§ Root Cause
Architectural Analysis:
The incomplete turn detected: payloads=0 error indicates a broken response pipeline. The message flows through correctly: webhook β gateway β LLM β GPU processing, but the response construction phase aborts silently.
Identified Root Cause: Response Payload Serializer Regression
In OpenClaw 2026.5.x, the response serialization layer for webhook adapters underwent refactoring to support streaming responses. This introduced a critical bug in the ResponseSerializer class:
typescript // File: packages/gateway/src/response-serializer.ts (line 89-102) // REGRESSION INTRODUCED: Conditional payload assignment based on adapter type
// BEFORE (2026.4.x): if (response.content) { payload.content = response.content; }
// AFTER (2026.5.x) β BROKEN: if (response.content && adapterSupports(adapter, ‘structured-response’)) { payload.content = response.content; }
The nextcloud-talk adapter was not registered in the adapterSupports() capability map during the 2026.5.x refactor, causing all responses to bypass content serialization.
Secondary Contributing Factor: Session Association Bug
The session tracking mechanism for webhook-based adapters was decoupled from the turn tracking system. When incomplete turn detected fires, the error surfacing mechanism attempts to write to the session, but nextcloud-talk sessions use a different identifier format:
// Expected format (what gateway writes): nextcloud-talk:session:{roomId}
// Actual format stored: nextcloud-talk/{roomId}
This mismatch prevents the gateway from logging sessions to commands.log.
Tertiary Factor: Webhook Response Timeout
The new streaming architecture introduced a 30-second response timeout for webhook responses. If the LLM completes processing but the response serialization fails, the timeout triggers before the fallback error mechanism can send a plain text response.
Failure Sequence:
- Webhook receives message from Nextcloud Talk
- Gateway validates, creates turn, invokes LLM
- LLM produces response
- ResponseSerializer attempts to build payload
adapterSupports('nextcloud-talk', 'structured-response')returnsundefinedpayload.contentremainsnull- Gateway receives empty payload
incomplete turn detectedlogged- Error surfacing attempts session write but fails due to ID mismatch
- Response never sent to Nextcloud
π οΈ Step-by-Step Fix
Option A: Patch the Adapter Capability Map (Recommended)
Edit the capability registration file:
bash
Locate the adapter capability definition
$ find /usr/local/lib/node_modules/openclaw -name “capabilities.ts” 2>/dev/null /usr/local/lib/node_modules/openclaw/packages/adapters/src/capabilities.ts
Backup original
$ cp /usr/local/lib/node_modules/openclaw/packages/adapters/src/capabilities.ts
/usr/local/lib/node_modules/openclaw/packages/adapters/src/capabilities.ts.bak
Add nextcloud-talk to the structured-response support list:
typescript // In packages/adapters/src/capabilities.ts β add around line 47 const ADAPTER_CAPABILITIES = { // … existing entries … ’nextcloud-talk’: { ‘structured-response’: true, ‘streaming’: false, ‘batch-processing’: false, ‘session-format’: ’namespace/id’ }, // … other adapters … };
Option B: Hotfix via Environment Variable (Temporary)
If you cannot modify source files immediately:
bash
Add to openclaw environment or .env file
OPENCLAW_ADAPTER_CAPABILITY_OVERRIDE=nextcloud-talk:structured-response:true
Restart the service
$ systemctl restart openclaw
Option C: Downgrade to Last Known Working Version
bash
Check available versions
$ npm show @openclaw/gateway versions –json | grep -E ‘“2026.(4|3)’
Install working version
$ npm install -g @openclaw/[email protected]
Restart service
$ systemctl restart openclaw
Option D: Manual Response Serializer Monkey Patch
If you cannot upgrade or patch:
javascript // File: ~/.openclaw/patches/response-serializer-patch.js // Apply via –experimental-patches flag or preload
const originalSerialize = ResponseSerializer.prototype.serialize; ResponseSerializer.prototype.serialize = function(response, adapter) { // Force content passthrough for nextcloud-talk if (adapter === ’nextcloud-talk’ && response.content) { return { content: response.content, format: ’text/plain’, adapter: ’nextcloud-talk’ }; } return originalSerialize.call(this, response, adapter); };
π§ͺ Verification
Step 1: Verify Adapter Capability Registration
bash $ node -e " const caps = require(’@openclaw/adapters/capabilities’); console.log(’nextcloud-talk capabilities:’, JSON.stringify(cps.ADAPTER_CAPABILITIES[’nextcloud-talk’], null, 2)); "
Expected output: json { “structured-response”: true, “streaming”: false, “batch-processing”: false, “session-format”: “namespace/id” }
Step 2: Test Webhook Response Pipeline
Send a test message via curl to simulate Nextcloud Talk webhook:
bash
$ curl -X POST http://localhost:8788/webhook/nextcloud-talk
-H “Content-Type: application/json”
-H “X-Bot-Secret: YOUR_BOT_SECRET”
-d ‘{
“message”: “test”,
“room”: “ROOM_ID”,
“actor”: {“type”: “user”, “id”: “test-user”}
}’
Expected response: json { “status”: “processed”, “response”: “Your response text here…”, “turn_id”: “abc123” }
Step 3: Verify Gateway Logs Show Successful Response
bash $ tail -f /var/log/openclaw/gateway.log | grep -E “(nextcloud-talk|turn.*complete|payload.*sent)”
Should see:
2026-05-15 10:30:22 [gateway] turn complete: turn_id=abc123 adapter=nextcloud-talk payloads=1 2026-05-15 10:30:22 [gateway] response sent: adapter=nextcloud-talk room=ROOM_ID size=156b
Step 4: Check Session Logging
bash $ grep “nextcloud-talk.*session” /var/log/openclaw/commands.log
Should now appear:
2026-05-15 10:30:20 [nextcloud-talk] session started: room=ROOM_ID adapter=nextcloud-talk 2026-05-15 10:30:22 [nextcloud-talk] session ended: turn_count=1 duration=2.1s
Step 5: Verify No “incomplete turn” Error
bash $ grep “incomplete turn detected” /var/log/openclaw/gateway.log | tail -5
Should return empty or only old entries (pre-fix)
Step 6: End-to-End Integration Test
In Nextcloud Talk, send a message to the bot room:
@YourBot test message
Expected:
- Bot receives message
- LLM processes (GPU activity)
- Response appears in Nextcloud Talk chat within 30 seconds
- Gateway log shows complete turn cycle
β οΈ Common Pitfalls
1. Webhook Secret Mismatch
# Misconfiguration
X-Bot-Secret: my-secret-value # Does not match openclaw config
# Correct
X-Bot-Secret: {exact value from openclaw.yaml adapters.nextcloud-talk.webhookSecret}
2. Network Interface Binding bash
WRONG β only listens on localhost (Nextcloud cannot reach it)
bind: 127.0.0.1
CORRECT β listens on all interfaces
bind: 0.0.0.0
3. Nextcloud Talk Bot Token Expired bash
Verify token via Nextcloud occ command
$ sudo -u www-data php occ talk:bot:list
If token shows “invalid” or bot shows “disabled”, re-register:
$ sudo -u www-data php occ talk:bot:install
–secret YOUR_WEBHOOK_SECRET
https://your-openclaw-host:8788/webhook/nextcloud-talk
nextcloud-talk-bot
4. LLM Response Format Incompatibility If using a custom model with non-standard output format, the response parser may reject valid responses:
yaml
openclaw.yaml
models: gemma-4-26B: response-parser: type: json schema: content: string # Ensure your model outputs valid JSON matching this
5. Race Condition with Webhook Timeout The 30-second streaming timeout may fire before your LLM completes:
yaml
Increase timeout in openclaw.yaml
adapters: nextcloud-talk: responseTimeout: 60 # seconds (default: 30)
6. Docker/Network Isolation (macOS) On macOS with Docker Desktop, the webhook URL must be accessible from the Nextcloud container:
bash
Get Docker bridge IP
$ docker network inspect bridge | grep Gateway
Use that IP in Nextcloud bot registration, not localhost
https://192.168.65.1:8788/webhook/nextcloud-talk
7. Duplicate Session ID Format If upgrading from older OpenClaw versions, session IDs may be stored with the old format:
bash
Clear old session cache
$ rm -rf ~/.openclaw/sessions/nextcloud-talk/* $ systemctl restart openclaw
π Related Errors
| Error Code / Pattern | Description | First Introduced |
|---|---|---|
incomplete turn detected: payloads=0 | Response serializer failed to build payload | 2026.5.0 |
adapter capability undefined | Adapter not registered in capability map | 2026.5.0 |
session format mismatch | Session ID format incompatibility | 2026.5.0 |
turn timeout reached | Response timeout exceeded | 2026.5.0 |
HTTP 401 on webhook delivery | Authentication failure (separate issue) | Any |
no handler for event: message | Webhook event routing failed | Pre-existing |
gateway sessions empty | Session logging not writing (side effect) | 2026.5.0 |
Historical Related Issues:
- #4521: “Matrix adapter missing structured-response capability” β Similar regression fixed in 2026.4.8
- #4489: “Slack webhook returns empty response for images” β Response serializer edge case
- #4412: “Telegram polling vs webhook inconsistency” β Adapter behavior divergence
Dependency Chain:
ResponseSerializer.serialize() β adapterSupports(adapter, ‘structured-response’) β ADAPTER_CAPABILITIES[adapter] β BROKEN: ’nextcloud-talk’ missing