{"record":{"id":"aadf84ed2cd584cc","repo":"affaan-m/ECC","slug":"tmux-split-window-did-not-return-a-pane-id-for-w","errorCode":null,"errorMessage":"tmux split-window did not return a pane id for ${workerPlan.workerName}","messagePattern":"tmux split-window did not return a pane id for (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/tmux-worktree-orchestrator.js","lineNumber":550,"sourceCode":"        'send-keys',\n        '-t',\n        plan.sessionName,\n        buildSessionBannerCommand(plan.sessionName, plan.coordinationDir),\n        'C-m'\n      ],\n      { cwd: plan.repoRoot }\n    );\n\n    for (const workerPlan of plan.workerPlans) {\n      const splitResult = runCommandImpl(\n        'tmux',\n        ['split-window', '-d', '-P', '-F', '#{pane_id}', '-t', plan.sessionName, '-c', workerPlan.worktreePath],\n        { cwd: plan.repoRoot }\n      );\n      const paneId = splitResult.stdout.trim();\n\n      if (!paneId) {\n        throw new Error(`tmux split-window did not return a pane id for ${workerPlan.workerName}`);\n      }\n\n      runCommandImpl('tmux', ['select-layout', '-t', plan.sessionName, 'tiled'], { cwd: plan.repoRoot });\n      runCommandImpl('tmux', ['select-pane', '-t', paneId, '-T', workerPlan.workerSlug], {\n        cwd: plan.repoRoot\n      });\n      runCommandImpl(\n        'tmux',\n        [\n          'send-keys',\n          '-t',\n          paneId,\n          `cd ${shellQuote(workerPlan.worktreePath)} && ${workerPlan.launchCommand}`,\n          'C-m'\n        ],\n        { cwd: plan.repoRoot }\n      );\n    }","sourceCodeStart":532,"sourceCodeEnd":568,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/tmux-worktree-orchestrator.js#L532-L568","documentation":"Thrown after `tmux split-window -d -P -F #{pane_id} -t <session> -c <worktree>` returns exit 0 (so runCommand did not throw) but splitResult.stdout.trim() is empty, meaning tmux did not emit the requested pane id. The orchestrator needs that id to select-pane, title it, and send-keys into the new worker, so it cannot continue without it. A blank pane id almost always indicates an incompatibility between the flags given and the installed tmux version/config, or stdout being swallowed.","triggerScenarios":"Old tmux (< 2.6, where split-window -P / -F #{pane_id} is unsupported or behaves differently). A tmux config (e.g. a hook like 'after-split-window' or set -g default-command / aggressive output) writing to stdout and corrupting the -F stream. A patched/distro tmux that ignores -P. The target session dying between new-session and the split so the split silently no-ops on some builds.","commonSituations":"Dev box on an old Ubuntu/Debian with tmux 2.x. Custom ~/.tmux.conf with pane-border-status or hooks that emit text. Containers where tmux was installed via a minimal package that strips format support. CI image pinned to an ancient tmux.","solutions":["Check the version: `tmux -V`. The orchestrator requires a modern tmux (>= 3.0 recommended); upgrade via your package manager if older.","Test the flag combo directly: `tmux new -d -s t && tmux split-window -d -P -F '#{pane_id}' -t t` — if it prints nothing, the problem is your tmux build/config, not the script.","Temporarily move ~/.tmux.conf aside and retry; if it works, a hook/option in your config is intercepting the split.","Ensure the session named in plan.sessionName is still alive right before the worker loop (a dead session makes split-window produce no pane)."],"exampleFix":"// before\nconst splitResult = runCommandImpl('tmux',\n  ['split-window', '-d', '-P', '-F', '#{pane_id}', '-t', plan.sessionName, '-c', workerPlan.worktreePath],\n  { cwd: plan.repoRoot });\nconst paneId = splitResult.stdout.trim();\nif (!paneId) throw new Error(`tmux split-window did not return a pane id for ${workerPlan.workerName}`);\n\n// after — fall back to listing panes of the session when -P/-F yields nothing\nlet paneId = splitResult.stdout.trim();\nif (!paneId) {\n  const list = runCommandImpl('tmux',\n    ['list-panes', '-t', plan.sessionName, '-F', '#{pane_id}:#{pane_current_path}'],\n    { cwd: plan.repoRoot });\n  paneId = list.stdout.trim().split('\\n')\n    .find(line => line.endsWith(`:${workerPlan.worktreePath}`))\n    ?.split(':')[0] || '';\n}\nif (!paneId) throw new Error(`tmux split-window did not return a pane id for ${workerPlan.workerName}`);","handlingStrategy":"try-catch","validationCode":"// Validate the tmux build supports the split-window -P -F combination up front.\nconst { spawnSync } = require('child_process');\nfunction tmuxSupportsSplitFormat() {\n  const v = spawnSync('tmux', ['-V'], { encoding: 'utf8' }).stdout?.trim();\n  const m = /tmux (\\d+)\\.(\\d+)/.exec(v || '');\n  if (!m) return false;\n  return Number(m[1]) > 3 || (Number(m[1]) === 3 && Number(m[2]) >= 0) || Number(m[1]) >= 3;\n}\n// Call before executePlan; bail with a clear message if false.","typeGuard":"// runtime.ensure the split result carries a pane id before relying on it.\n/**\n * @param {{ stdout?: string, status?: number|null }} r\n * @returns {string|null}\n */\nfunction paneIdFromSplit(r) {\n  const id = (r && typeof r.stdout === 'string' ? r.stdout : '').trim();\n  return /^%\\d+$/.test(id) ? id : null;\n}","tryCatchPattern":"try {\n  for (const workerPlan of plan.workerPlans) {\n    const splitResult = runCommandImpl('tmux', /* split-window args */, { cwd: plan.repoRoot });\n    const paneId = paneIdFromSplit(splitResult) ?? fallbackListPanes(plan.sessionName, workerPlan.worktreePath);\n    if (!paneId) throw new Error(`tmux split-window did not return a pane id for ${workerPlan.workerName}`);\n    // ... use paneId\n  }\n} catch (err) {\n  if (/did not return a pane id/.test(err.message)) {\n    throw new Error(`${err.message} — check tmux -V (>=3.0) and ~/.tmux.conf hooks.`);\n  }\n  throw err;\n}","preventionTips":["Require tmux >= 3.0 in your orchestrator wrapper and fail fast with a clear message.","Run executePlan with a clean HOME or isolated config to rule out ~/.tmux.conf hooks.","Add a fallback that lists panes by worktree path when -P/-F yields nothing.","Log the raw splitResult.stdout/stderr on failure to detect config interference."],"tags":["tmux","orchestration","version-compat","cli"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}