Skip to content

Moving a chat back and forth between computers makes its copy-folder name grow until it can't open (app/v3, land/app-v3) #2579

Open

Description

@AbirAbbas

What happened

On app/v3 (a687c92) and the land/app-v3 landing candidate (8511037), moving one chat back and forth between two
paired computers makes the copy-folder name longer on every move. After about three moves, the computer taking the
chat cannot create the folder:

create session directory: mkdir <home>/v3/projects/<very long name>: file name too long

The take has already acquired the hold, so the chat's turns are safe on the sync service and the other computer can
still take it back. On the computer that just took it, though, the window stays on Home and the chat cannot open.

dev b0dfe97 is not affected. Its take path files copies through takeCopyBucket, which hashes any name over
255 bytes, and reuses an existing local root. On app/v3 and land/app-v3, chatContinuer builds the bucket inline
with no cap, so land/app-v3 brings the bug to dev when it lands, unless the fix lands with it. #2287 contains a fix:
keep existing roots, reuse an origin copy's own name, and cap names at 240 bytes with a stable hash.

Replication

Deterministic (no model).

  1. make build, go build -o modelstub ./test/remote/stub, and a local sync service:
    bin/codeaf relay --listen <address> --store <empty folder>.
  2. Make two throwaway homes, A and B. Each sets its own HOME, CODEAF_HOME and CODEAF_PROFILE_DIR, plus
    CODEAF_SYNC_URL=<address>, CODEAF_BASE_URL=<stub address>/api/v1 and OPENROUTER_API_KEY=stub, with
    {"model.talk":"stub/scripted"} in the config. Pair them with codeaf pair --code / codeaf pair <code>.
  3. On A, start a chat in an ordinary project folder (for example /tmp/w/project) and send PROBE-ECHO one.
  4. Alternate Continue here on Home: B takes it and sends PROBE-ECHO, then A takes it back and sends, and so on, three
    or four moves.
    • Today (app/v3, land/app-v3): a move fails with the file name too long line above on the taker, and the window
      stays on Home.
    • Expected: every move opens the chat. Folder names stay bounded and stable across moves.

Where

  • chatContinuer's BucketFor in cmd/codeaf/synctake.go (search "copy-"+encodeWorkspace(origin)). Compare
    takeCopyBucket in the same file, which caps the name.
  • Separate note, every build including dev: v3ProjectDir in cmd/codeaf/chatv3_layout.go names a chat's project
    folder encodeWorkspace(workspace) with no length cap. A copy whose work folder path is very long (long home and
    project paths) can exceed 255 bytes there on a single move, even with the bucket capped. That case does not grow per
    move.

The fix

Bound every name derived from a path:

  • the copy bucket: reuse an existing root, reuse an origin copy's own name, and cap with a stable hash;
  • the project folder name, with the same cap.

Folder names must not change for chats that already have short ones.

Acceptance

  • e2e: two homes on a local sync service (stub model), ten alternating moves of one chat. Every move opens the chat,
    every journal is exact, and no folder-name component exceeds 255 bytes.
  • e2e: the control. Existing short folder names stay the same after the change.
  • Unit: the bucket and project-folder names are bounded and stable for a nested copy origin, and unchanged for a
    short one.

Activity

  1. AbirAbbas commented on Nov 10, 2026

    @AbirAbbas
    CollaboratorAuthor

    Two notes from the fix (e70a2c1a4, which lands with the next dev re-merge into land/app-v3). Both were read from code at land 8c06212 and dev ace7888, not run.

    1. A take that fails after the hand-over leaves the hold with the taker until the lease lapses.

    • Inside the take, every failure gives the hold back. That covers filing the copy, restoring task workspaces, adopting the workspace identity, and recording the send lease.
      • On land this goes through giveBack (internal/syncsetup/take.go:399, :433-437).
      • On dev it calls Dir.Release directly (internal/syncsetup/take.go:346-389).
    • After the take returns, nothing gives the hold back if opening the chat fails.
      • App road (land only; dev has no cmd/codeaf/app_take.go): landTake records the chat as this computer's "even if opening it fails" (cmd/codeaf/app_take.go:177-178). An appTakeOpen error then returns with no release (:202-204).
      • Terminal road (dev and land alike): releaseUnusedTake (land internal/tui3/homepanel_continue.go:629, dev :615) runs only for an automatic take that was cancelled or settled (internal/tui3/conversation_open.go:132-134, land homepanel_continue.go:621-624). It does not run when opening fails for a take the person chose.
    • What ends it. Only an open chat's drive renews a lease: it heartbeats and names the lease on the holding socket (internal/syncsetup/drive.go:49, :410).
      • A take whose open failed has no drive, so nothing renews its lease.
      • The lease lapses after LeaseTTLMillis = 90 s (internal/clusterstate/types.go:10), and the other computer can then continue the chat again.
      • So it heals itself within about 90 seconds rather than blocking for good.
    • The obvious fix, for a later wave: give the hold back when opening fails. On the terminal road that is ReleaseUnused; the app road needs the same in appTake.

    2. v3ProjectDir is still uncapped (note only).

    • encodeWorkspace(workspace) (cmd/codeaf/chatv3_layout.go:65) has no 255-byte cap, so a workspace whose encoded name is over 255 bytes still fails at os.MkdirAll with create session directory: … (:66-67).
    • Now that the copy folder is capped, repeated moves no longer grow the name this function sees. Only a long local workspace path trips it.
  2. added a commit that references this issue on Nov 10, 2026
    3cb458b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions