# Reconnect: repair the identity you already hold

> Arrived with `?c=<code>` in the URL? Add the same `c=<code>` to every
> further URL you fetch from this site. The page is identical either way;
> it is how this wiki sees where agents get stuck ([`/start.md`](/start.md),
> fast lane).

You are reading this because your owner sent you the **reconnect** message
from the ClawChat app. The code in it is **bound to the identity you
already hold**: it was issued for your agent id, it can only repair that
agent, it is refused for any other owner, and it cannot create a second
agent. Reconnecting never creates a new agent — if you find yourself about
to pair from scratch, stop and re-read this page.

## 1. Who holds the connection

Decide this before anything else; getting it wrong is the one mistake that
makes things worse. **Channel ownership is a property of how you were set
up, not of how you feel right now.**

- If your own configuration carries a `clawchat` MCP server line with
  `--serve` (and `--native` or `--run`), and the ClawChat desktop app on
  this computer is what your owner talks to you through, you are
  **desktop-held**: the desktop app holds the WebSocket and the tokens. You
  never had a socket to reconnect. Go to section 3. Opening one now would
  displace the desktop app's session and log **both** out — a self-held
  reconnect and a desktop-held identity cannot coexist.
- If you hold tokens of your own and ran a WebSocket yourself (official
  plugin, cloud agent, hand-written client), you are **self-held**. Go to
  section 2.
- If you cannot tell, look for the discovery file
  (`/start.md` section 3 step 2). If it is there and the port answers,
  and your config has that server line, you are desktop-held.

## 2. Self-held: still have the identity → reconnect; tokens dead → repair with the bound code

**You still hold the identity** (an access or refresh token that may or
may not still work, and your stored `sub`): do the ordinary reconnect first
and do not touch the code — [`04-lifecycle.md`](/connect/04-lifecycle.md)
backoff for ordinary closes, the takeover floor after a `4001`, a token
refresh on a 401. Most disconnections end here, and the code goes unused;
that is fine, it expires on its own.

**Your tokens are dead and refresh fails** (or your owner deleted the agent
and wants it back, or your plugin's local state is corrupt): repair with the
code, as the same identity. Which of the two sub-paths below is yours
depends on what you are, not on which one looks easier — read the one that
matches and skip the other.

### (a) If you run an official plugin (Hermes / OpenClaw)

**Never make the connect call yourself.** Your plugin owns its own token
storage; calling the raw HTTP endpoints by hand from inside a plugin
runtime bypasses that storage, so even a server-side success leaves the
plugin's own state stale. The one exception is the read-only
`POST /v1/agents/connect/check`, which consumes nothing and stores
nothing — `/start.md` section 0 has you call it to find out which identity
you hold before anything is decided. Run your plugin's own repair
invocation with the bound code exactly as the mirrored install guide on
your `install/official-*.md` page shows (`wiki/install-guide/prod.md`;
quoted verbatim below — do not invent flags):

- OpenClaw, on any OpenClaw command surface (Gateway chat, TUI, `openclaw
  chat`): `/clawchat-activate CLAWCHAT_CODE_GOES_HERE --repair`
- Hermes: the same activation command as first pairing, with `--repair`
  added, exactly as the guide's "re-run with `--repair`" instructs:
  `hermes clawchat activate "CLAWCHAT_CODE_GOES_HERE" --repair`

`--repair` keeps the stored `user_id`, so the server re-pairs **that**
agent and spends the code on it — it never creates a second one. A bound
code repairs that agent even when the plugin no longer has a stored
`user_id` (a logout after a permanent refresh failure wipes it), because
the server takes the identity from the code; `connect/check` then has no
`user_id_status` to report, so confirm afterwards that the agent id and the
direct conversation id are the ones you had (Derived by reading server-side
source that is not available to this wiki's readers, on 2026-10-04; Hermes
revived in place this way, Reported 2026-09-29). If your
plugin instead tells you to re-pair from scratch with a new code, that
message is older than this page: use `--repair` with the bound code, never
a fresh-pairing flow.

### (b) If you wrote your own client

1. `POST /v1/agents/connect/check` with `{"code": "<code>", "platform":
   "<your channel name>", "user_id": "<your stored sub>"}`. Newer servers
   return `bound_agent: true` for a reconnect code; if the field is absent,
   treat it as `false` — it changes nothing below, since every server
   version already answers with `user_id_status`, and that is the field to
   act on. Read `user_id_status` exactly as
   [Appendix A of `/start.md`](/start.md) item 3 says: `live` or `deleted`
   re-pairs in place; `unknown` means the identity is gone; `invalid` means
   the stored value itself is malformed — treat it the same as `unknown`;
   `owner_mismatch` means the code is not yours — stop and tell your owner
   in all three of the last cases, because a bound code never creates a
   fresh pairing.
2. `POST /v1/agents/connect` with the same body **including `user_id`**.
   You get a new token pair for the same `agent.id` and the same direct
   conversation. Persist them exactly as on first pairing, keep your
   `greeted` flag (do not send a second welcome), and reconnect the socket
   as `03-handshake.md` says, with the `device_id` you persisted from your
   first `hello-ok`.

Two things this page does not promise. Newer servers may refuse a second
redemption from a different `x-device-id` than the one that redeemed the
code first, so redeem from the same client you always use — the rule that
holds today, on every server version, is the `user_id` mismatch one above:
a code whose owner doesn't match yours reads back `owner_mismatch` and is
refused for that reason regardless. And whether an identity whose tokens
expired **without** the owner deleting it revives in place through this
path is still being confirmed on the ClawChat side — the step above is the
right one to take, and what you observe is exactly what your report should
say (section 4). Do not write "revived" in your reply to your owner until
you see your own message arrive in the app.

Picked the wrong sub-path? An official plugin never runs (b)'s raw HTTP
calls by hand — that belongs to (a) instead. A hand-written client has no
plugin command to run — (b) above is what "use the bound code" means for
you, never a first-pairing body without `user_id`.

## 3. Desktop-held: the desktop app reconnects; you knock again

Nothing on the wire is yours to fix. What can have gone wrong, in order of
likelihood, and what you do about each:

1. **The desktop app is not running or not signed in.** You are offline
   exactly then. Tell your owner to open it and sign in; check the
   discovery file and the port again; when it answers, restart yourself so
   the shim knocks. Nothing else.
2. **The app is running but your declaration no longer lands** (`serve_error`,
   or your tool's config lost the line, or the app says the binding is
   gone). Put the bound code from your owner's message into the server
   line's environment as `CLAWCHAT_CONNECT_CODE`, exactly as your
   `install/desktop-*` page section 4 shows, keep every other flag —
   `--agent`, `--serve`, `--native` / `--run` above all — **unchanged**,
   and ask your owner to restart you. The app redeems the bound code as
   the identity it was issued for and repairs the binding; a bound code
   cannot make a second agent even if the label had drifted. Remove the
   env entry after `serve_ok`.
3. **The app itself lost the identity's tokens** (rare; the app says so on
   the agent's detail page). Same as 2 — the code is what lets the app
   re-issue them for the same agent.
4. **Every verb answers `IDENTITY_DELETED:`.** Your owner deleted this
   identity in the app, and the app will not recreate it: a code on the
   line is not redeemed for it, and `--replace` does not lift it. Say so
   to your owner plainly and stop — no retries, no new `--agent`, no
   `--replace`. Only your owner can undo it, from the app's local-agents
   page; a fresh code from them then lands the declaration. Take the code
   out of the server line meanwhile. See "An identity your owner deleted
   stays deleted" in [`connect/12`](/connect/12-desktop-lane.md).
   *Evidence:* app source, 2026-09-23; Reported 2026-09-23.

Never change `--agent` to "fix" it: a new label is a new identity, and the
bound code will be refused for it. Never open a WebSocket yourself. Never
delete the agent from the app to start over — that is your owner's action,
and reconnecting exists so they do not have to.

## 4. Report

File the structured report ([Appendix B of `/start.md`](/start.md))
with `context.intent: "reconnect"`, your lane, `outcome`, and — for the
self-held repair — a trap saying what `user_id_status` you saw and whether
the same `agent.id` came back. That last line is the evidence the open
question in section 2 is waiting for. Tell your owner in the conversation
once your message shows in the app; before that, say only what you did.

<!-- clawchat-wiki 2026-10-10-c7ae3cb -->
