Skip to content

Bug: Interactive engine fails to start for any project with an AGENTS.md larger than ~16 KB (tmux "command too long") #96

Description

@malys

Summary

runInteractiveSingle() passes the entire system prompt — which since #54 includes
the project's AGENTS.md, allowed up to MAX_BYTES = 65536 — as an argv string
inside the command handed to tmux new-session. tmux refuses any command string
longer than ~16 KB (its imsg limit), so the session is never created and the user
sees only:

failed to start tmux session for interactive engine

The real error, command too long, is lost because tmux is spawned with
stdio: 'ignore' (claude-interactive.js:379) and only tmuxHasSession() is checked.

Repro

Project with a 24 807-byte AGENTS.md and no CLAUDE.md (real case: a monorepo
whose conventions file is ~25 KB), opened in the studio with the interactive engine.

const { agentsMdPreamble } = require('/app/agents-md.js');
const p = agentsMdPreamble('/proj');            // preamble bytes: 24972
const cmd = `env -u CLAUDECODE claude --session-id x --model sonnet \
--dangerously-skip-permissions --append-system-prompt ${shq(p)}`;
// innerCmd bytes: 25125
$ tmux new-session -d -s diag "$cmd"
command too long
$ echo $?
1

Measured threshold on the same tmux build: 16 000 B → ok, 16 333 B → ok,
16 334 B → command too long. So the ceiling is the tmux command, not argv
(ARG_MAX ~2 MB on Linux), which is what MAX_BYTES = 65536 in agents-md.js
is sized against.

Anything else appended to the prompt (studio system prompt, mode preamble)
counts toward the same 16 KB, so a smaller AGENTS.md can hit this too.

Expected

The interactive engine starts regardless of instruction-file size, or fails with
the actual tmux error.

Suggested fix

Spill the prompt to a file and let the shell inside tmux read it back — same
pattern as the existing mcpConfigPath() (claude-interactive.js:239-251):

// claude-interactive.js
function systemPromptPath(sp) {
  const hash = crypto.createHash('sha256').update(sp).digest('hex').slice(0, 16);
  const p = path.join(os.tmpdir(), `ccs-sp-${hash}.txt`);
  if (!fs.existsSync(p)) fs.writeFileSync(p, sp, { mode: 0o600 });
  return p;
}

// line 374
if (sp) innerCmd += ` --append-system-prompt "$(cat ${shq(systemPromptPath(sp))})"`;

Verified locally: innerCmd drops from 25 125 to 68 bytes, the session starts,
and the prompt reaches the child byte-identical (only the trailing newline is
stripped by $( )).

Two smaller items worth fixing alongside:

  1. Surface the tmux error. Spawning with stdio: 'pipe' and including stderr in
    the wsSend({ type: 'error', ... }) payload turns this class of failure from
    a 2-hour bisect into one line on screen.
  2. MAX_BYTES = 65536 in agents-md.js documents itself as safe against argv
    ceilings; with the prompt travelling through a tmux command the effective
    ceiling is ~16 KB, so the comment is misleading even after the fix above
    removes the limit.

Activity

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

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions