Self-hosting a WeChat ⇄ LLM bridge: the five things that kill DIY setups

Why your WeChat AI bot works for an hour then goes silent, how to do multi-user sessions, and the failover config that keeps it alive.

Getting a message from WeChat to a language model and back is a weekend project. Keeping it alive for a month is the hard part. These are the five failures that reliably appear, in the order they appear.

1. The callback URL dies and nothing tells you

Most guides use cloudflared tunnel --url ..., which gives a *.trycloudflare.com hostname that changes whenever the process restarts. The platform keeps posting to the old host, the tunnel DNS no longer resolves, and the symptom is "messages go out, no replies ever come back".

Fix: named tunnel with your own domain, and a watchdog that compares the current hostname with the last seen one and writes an alert file when it changes.

2. One global conversation instead of per-user sessions

If the session key is not derived from the sender id, every user shares one context: person B's question continues person A's conversation. It looks like the bot has gone insane.

const session = store[fromUserId] || { turns: [] };
session.turns.push({ role: "user", content });
const reply = await ask(session.turns.slice(-40));
session.turns.push({ role: "assistant", content: reply });

Persist to disk, cap the turns, and reset after an idle window — otherwise memory grows forever and one chatty user burns your token budget.

3. No watchdog, so it stays dead

Whatever kills a long-running Node process — OOM, an unhandled rejection, a Windows update — the result is the same: silence. A health endpoint plus a two-minute scheduled check that restarts it is the difference between a demo and a product.

curl -fsS http://127.0.0.1:8787/health || systemctl restart llm-bridge

4. Failover added to one path but not the other

The classic bug: failover is implemented in the HTTP API route, but the bot path calls the provider directly — and the bot is the path that matters. Put a single askWithFailover(messages) in front of every entry point, demote a failed provider for N minutes, and log one line per switch (FAILOVER primary -> fallback after 8.1s).

5. Waiting out the relay's 180-second timeout

Third-party relays time out upstream around three minutes. Without streamIdleTimeoutMs, every failure costs you three minutes of a user staring at "typing…". Set it to ~90s, retry once, then fail over.

Deployment note that costs hours

If you write PowerShell scripts, do not put non-ASCII characters in a UTF-8 file without a BOM. PowerShell 5.1 decodes it as GBK, swallows the following line break, and the next line of code becomes part of the comment. You get a phantom "missing closing brace" with no line number. Save with BOM, or keep scripts ASCII.

Packaged version

WeChat ⇄ LLM Bridge is the deployment kit: runnable skeleton with signature verification, per-user sessions, dual-provider failover, /health and /restart, a watchdog script, systemd and Task Scheduler units, and a written troubleshooting playbook. 39 USDT.

WeChat ⇄ LLM Bridge

Self-hosted bridge so you can chat with any OpenAI-compatible model inside WeChat/WeCom.

Get it — 39 USDT