You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
A line to a friend's Bot that is gone fails with a raw, doubled reason and an unusable Retry #954
From the product self-test routine (real web UI, two isolated homes on main ed60c5a1).
Steps
Home A shares its Bot with home B; B talks to it in the normal chat window and leaves that chat open.
That Bot stops existing for B (on A's side it is removed / no longer listed).
B types one more line and presses Enter.
Expected
One sentence in the failed card that names the Bot and says what to do next - the same conversation already prints exactly that sentence (<Bot> (@handle) is no longer shared with you, so messages to it cannot be delivered. … Ask its owner for a new invite.). Retry should be absent or disabled, because the Bot does not exist any more and Retry can only fail again.
Actual
The card says, twice in a row:
Not sent: no such remote bot. Try again. Try again.
no such remote bot is the Bot Social server's wording passed through unchanged: it names nothing, and a person cannot act on it.
Try again. appears twice.
A Retry button is offered on a failure that can never succeed; pressing it repeats the same card.
The friendly sentence quoted under Expected is printed separately in the same conversation, so the screen says both "no such remote bot" and "ask its owner for a new invite" about the same event.
Where it is
Failure text comes from the Bot Social error in plugins/social/src/social.js (the send path around the bot_not_shared / missing-remote handling, ~:1485-1510) and is pushed into the chat as a failed item.
Card: plugins/social/client.js:1933 (ChatApproval, data-state: 'failed') renders item.error verbatim and always adds the Retry button (data-flybot=hold-retry).
The good sentence lives in core/src/client/model-menu.js:246.
Evidence
Two homes (.dsh-test-a / .dsh-test-b), real Chromium, reports/ui-journey/sr3-02-b-sent-after-revoke.txt and reports/ui-journey/sr3-03-b-after-reload.txt in the self-test workspace. The same run confirmed the good sentence renders in the same pane, so both strings are one event.
Scope
Copy plus one decision (Retry or not) - plugins/social/client.js and the error the send path records; no protocol change.
Not filed before: searched no such remote bot, no longer shared and Not sent - no open or closed issue matches.
Triage (deduped against #428, #413, #886, #881: all closed, none covers the terminal phrase or the doubled step).
What the person sees
Not sent: no such remote bot. Try again. Try again.
[Retry]
and one line below, the chat dock for the same Bot reads … is no longer shared with you … Ask its owner for a new invite. (core/src/client/i18n.js:284, drawn by RemoteGoneDock in core/src/client/model-menu.js:241-246, chosen at :199 when bot.remote.gone).
Where it comes from — two independent layers
plugins/social/src/social.js:1524userFacingSendError() has branches for paused, not-shared, room-not-shared, no_relationship, grants, connection, 401/403/429, timeout — and nothing for "this handle is not on that home any more". The server's raw reason (no such remote bot / unknown remote bot) falls through to :1546-1551: short = (body || raw).slice(0, 100).replace(/\.$/, '') then return new Error(\${short}. Try again.`). So the phrase handed upward is no such remote bot. Try again.`.
core/src/host/remote-chat.js:25notSentLine() receives that sentence, matches none of its classifiers (:31-62), keeps it as short and appends its own step at :64: return \Not sent: ${short}. Try again.`. Hence Try again.` twice.
The Retry is the failed-hold card: plugins/social/client.js:1929-1940 (item.status === 'failed') prints Not sent + item.error + the Retry button with data-flybot="hold-retry" at :1938. It calls onDecide(item, 'retry') for a failure that no retry can fix: only the owner sharing the Bot again helps, which is what the dock already says.
Expected
A terminal reason (no such remote bot, unknown remote bot, Bot no longer published) becomes one human sentence, the same wording the dock uses: the Bot is no longer shared, ask its owner for a new invite. No internal phrase, no second Try again..
No Retry on that card: retrying cannot succeed. A retryable failure (429, timeout, paused) keeps its Retry and its single Try again..
Neither layer may append a step to a sentence that already ends with one: notSentLine() and userFacingSendError() must both check before adding Try again..
Where to change
plugins/social/src/social.js:1524-1553 — add the terminal branch before the generic tail, and mark the error ({ retryable: false } or an existing flag) so the card can tell.
core/src/host/remote-chat.js:25-65 — classify the terminal reason, reuse the dock's sentence, and stop doubling the step.
plugins/social/client.js:1929-1940 — render Retry only when the item is retryable; otherwise no button.
Acceptance
Send to a Bot whose sharing was revoked → the notice is the dock's human sentence, exactly one step sentence, no token such as no such remote bot, send_as_member, or a raw lowercase reason id.
That card has no Retry button (data-flybot="hold-retry" absent).
Negative control: a 429 / timeout / paused failure still shows Retry and one Try again. — the fix must not remove the retry path globally.
notSentLine() over an input that already ends with Try again. yields one step, not two (unit case in core/test/remote-chat.test.js).
The change is mirrored in patches/ds-bot/files/ byte-identically for every touched core file (node scripts/ds-bot-patches.mjs check).
Notes
The same wording already exists in core/src/client/i18n.js:284; prefer reusing it over a new sentence.
Checked this fix through the real UI on main 2ab9e3928279 (the merge that carried #961, before later merges) — the card path behaves as asked: after the owner revokes, the new card reads
Chief (@chief.selftesta2.home) is no longer shared with you, so messages to it cannot be delivered. It runs on its owner's machine, so another model cannot stand in. Ask its owner for a new invite.
one short message, no "Try again. Try again.", no Retry button in that card.
One path is not covered: the inline failure line for the same situation still reads Not sent: peer has not granted chat.public. Try again. (internal capability name + a retry that can never succeed). Filed as #971 so this issue does not need to be reopened.
Activity
Birfy commentedon Oct 24, 2026
Triage (deduped against #428, #413, #886, #881: all closed, none covers the terminal phrase or the doubled step).
What the person sees
and one line below, the chat dock for the same Bot reads
… is no longer shared with you … Ask its owner for a new invite.(core/src/client/i18n.js:284, drawn byRemoteGoneDockincore/src/client/model-menu.js:241-246, chosen at:199whenbot.remote.gone).Where it comes from — two independent layers
plugins/social/src/social.js:1524userFacingSendError()has branches for paused, not-shared, room-not-shared, no_relationship, grants, connection, 401/403/429, timeout — and nothing for "this handle is not on that home any more". The server's raw reason (no such remote bot/unknown remote bot) falls through to:1546-1551:short = (body || raw).slice(0, 100).replace(/\.$/, '')thenreturn new Error(\${short}. Try again.`). So the phrase handed upward isno such remote bot. Try again.`.core/src/host/remote-chat.js:25notSentLine()receives that sentence, matches none of its classifiers (:31-62), keeps it asshortand appends its own step at:64:return \Not sent: ${short}. Try again.`. HenceTry again.` twice.The Retry is the failed-hold card:
plugins/social/client.js:1929-1940(item.status === 'failed') printsNot sent+item.error+ theRetrybutton withdata-flybot="hold-retry"at:1938. It callsonDecide(item, 'retry')for a failure that no retry can fix: only the owner sharing the Bot again helps, which is what the dock already says.Expected
no such remote bot,unknown remote bot, Bot no longer published) becomes one human sentence, the same wording the dock uses: the Bot is no longer shared, ask its owner for a new invite. No internal phrase, no secondTry again..Retryon that card: retrying cannot succeed. A retryable failure (429, timeout, paused) keeps its Retry and its singleTry again..notSentLine()anduserFacingSendError()must both check before addingTry again..Where to change
plugins/social/src/social.js:1524-1553— add the terminal branch before the generic tail, and mark the error ({ retryable: false }or an existing flag) so the card can tell.core/src/host/remote-chat.js:25-65— classify the terminal reason, reuse the dock's sentence, and stop doubling the step.plugins/social/client.js:1929-1940— render Retry only when the item is retryable; otherwise no button.Acceptance
no such remote bot,send_as_member, or a raw lowercase reason id.Retrybutton (data-flybot="hold-retry"absent).Retryand oneTry again.— the fix must not remove the retry path globally.notSentLine()over an input that already ends withTry again.yields one step, not two (unit case incore/test/remote-chat.test.js).patches/ds-bot/files/byte-identically for every touched core file (node scripts/ds-bot-patches.mjs check).Notes
core/src/client/i18n.js:284; prefer reusing it over a new sentence.Birfy commentedon Oct 24, 2026
Written-by: Dev2
Claiming this one. Branch
954-bot-gone-no-retry, cut from maind1052c0e; the fix and its tests are on the way as one PR.host: carry a final dispatch failure onto the hold as retry: false (#954
host: reuse the gone-Bot sentence and stop doubling "Try again." (#954)
social: turn bot_not_found into the one sentence and mark it noRetry (#…
social: draw no Retry on a failed hold that can never succeed (#954)
test: the gone-Bot not-sent line (#954)
test: retry: false reaches the chat, an ordinary failure keeps Retry (#…
2 remaining items
patches: mirror core/src/host/remote-chat.js (#954)
docs: the final-failure hold and the gone-Bot sentence (#954)
docs: Invariant 8 draws Retry only while it could work (#954)
changelog: a gone friend Bot reads one sentence, no Retry (#954)
patches: mirror core/test/remote-chat.test.js (#954)
patches: mirror core/test/host-approval-status.test.js (#954)
docs: a final dispatch failure reaches the card as retry: false (#954)
Birfy commentedon Oct 24, 2026
Checked this fix through the real UI on
main 2ab9e3928279(the merge that carried #961, before later merges) — the card path behaves as asked: after the owner revokes, the new card readsone short message, no "Try again. Try again.", no Retry button in that card.
One path is not covered: the inline failure line for the same situation still reads
Not sent: peer has not granted chat.public. Try again.(internal capability name + a retry that can never succeed). Filed as #971 so this issue does not need to be reopened.Read a gone friend's Bot as one sentence and drop the useless Retry (T…
ThinkFlowLab#968: one predicate decides whether a failed card can ret…