Repository navigation
Description
Description
Summary
The message ID counter (12-hex-digit component after the msg_ prefix) wrapped around from fffb back to 0035 on Aug 15, 2026 (after ~2 months of use). Because the session loop and MessageV2.latest() determine "newest message" via lexicographic string comparison of message IDs (not by timestamp), every message created after the wrap-around is considered older than all pre-wrap messages. As a result, any user message sent in a pre-existing session is silently ignored: the agent loop exits at step=0 without ever calling the model. Newly created sessions work fine (their IDs are internally consistent), which makes the bug confusing to diagnose.
Environment
- OS: Windows 10 (also likely platform-independent)
- opencode desktop app (
@opencode-aidesktop), version:[fill in your exact version from the app] - Data migrated from JSON storage to SQLite (
opencode.db) on Jun 16, 2026 - Provider/model:
[e.g. zhipuai / glm-5.2](irrelevant to the bug)
Steps to reproduce
- Use opencode daily for ~2 months (until the message ID counter passes
ffffand wraps to0000).- Faster repro on a scratch install: manually insert messages with IDs near
msg_ffff...and then send a new message.
- Faster repro on a scratch install: manually insert messages with IDs near
- Open a session created before the counter wrapped around.
- Send any message (e.g.
hello).
Actual behavior
- The user message is persisted (present in the
messagetable with its parts), but no assistant response is ever generated. - Log shows the loop exiting immediately without any LLM call:
level=INFO message=loop session.id=ses_XXXX step=0
level=INFO message="exiting loop" session.id=ses_XXXX ← ~10ms later
- Affects all sessions that contain pre-wrap messages. Restarting the app does not help.
- Sessions created entirely after the wrap-around behave normally.
Expected behavior
Sending a message in any session should always trigger a model response, regardless of ID ordering. Message recency should be determined by creation time, not by string comparison of IDs.
Root cause analysis
- Exit condition in
SessionPrompt.run(found inapp.asar,node-*.jschunk):
if (lastAssistant2?.finish && !["tool-calls"].includes(lastAssistant2.finish)
&& !hasToolCalls3 && lastUser.id < lastAssistant2.id) { // string comparison!
yield* exports_Effect.logInfo("exiting loop", { "session.id": sessionID });
break;
}MessageV2.latest()also selects the latest user/assistant message by ID comparison:
if (info32.role === "user" && (!user || info32.id > user.id)) user = info32;
if (info32.role === "assistant" && (!assistant2 || info32.id > assistant2.id)) assistant2 = info32;- Message IDs look like
msg_<12-hex-counter><random-suffix>. Evidence from my database (first 2-4 hex of counter vs. creation time):
| Period | ID prefix (hex) | Notes |
|---|---|---|
| Jun 16, 2026 (first message) | ed01 |
counter start after migration |
| Aug 14, 2026 (evening) | ffed … fffb |
approaching top |
| Aug 15, 2026 (morning) | 0035 |
wrapped around |
| Aug 15, 2026 (afternoon) | 0043 |
continues |
After the wrap, for any old session: lastUser.id (e.g. msg_0043...) < lastAssistant2.id (e.g. msg_fffb...) is always true, so the loop treats the new user message as already answered and exits.
Suggested fix
- Compare by
time_created(with the existing same-millisecondidtiebreaker, e.g. theolder()helper already used for cursor pagination), instead of raw string ID comparison inlatest()and the loop-exit condition. - And/or generate message IDs with a monotonically increasing counter that cannot wrap (e.g. wider counter, or a per-database sequence table).
Workaround (already applied locally)
I hot-fixed my database by remapping all 2610 pre-wrap message IDs into the 00000001xxxx low range (preserving order and random suffixes), cascading updates to part.message_id (10,704 rows), message.data.parentID (2,197), and in-JSON references (21). All affected sessions recovered immediately. Happy to share the Node script if useful for other affected users.
Additional context
- The same wrap-around will recur roughly every 2 months of active use until fixed upstream.
- Event-table history references were intentionally left untouched (do not affect the loop), so event replay may still show old IDs.
Plugins
No response
OpenCode version
v1.17.9
Steps to reproduce
No response
Screenshot and/or share link
No response
Operating System
No response
Terminal
No response
Activity
github-actions commentedon Aug 15, 2026
This issue might be a duplicate of existing issues. Please check:
000..., which sorts before all pre-wrap IDs;MessageV2.latest()and the loop exit condition both use lexicographic ID comparison, silently dropping new user input in sessions with history.lastUser.id < lastAssistant.idlexicographic comparison inrunLoopcausing the loop to never exit for imported sessions whose IDs aren't time-sortable.opencode-agent commentedon Aug 17, 2026
This was fixed in version 1.18.15. Please upgrade to 1.18.15 or newer.