{"record":{"id":"6987e450e24f5170","repo":"affaan-m/ECC","slug":"launchercommand-must-be-a-non-empty-string","errorCode":null,"errorMessage":"launcherCommand must be a non-empty string","messagePattern":"launcherCommand must be a non-empty string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/tmux-worktree-orchestrator.js","lineNumber":18,"sourceCode":"'use strict';\n\nconst fs = require('fs');\nconst path = require('path');\nconst { spawnSync } = require('child_process');\n\nfunction slugify(value, fallback = 'worker') {\n  const normalized = String(value || '')\n    .trim()\n    .toLowerCase()\n    .replace(/[^a-z0-9]+/g, '-')\n    .replace(/^-+|-+$/g, '');\n  return normalized || fallback;\n}\n\nfunction renderTemplate(template, variables) {\n  if (typeof template !== 'string' || template.trim().length === 0) {\n    throw new Error('launcherCommand must be a non-empty string');\n  }\n\n  return template.replace(/\\{([a-z_]+)\\}/g, (match, key) => {\n    if (!(key in variables)) {\n      throw new Error(`Unknown template variable: ${key}`);\n    }\n    return String(variables[key]);\n  });\n}\n\nfunction shellQuote(value) {\n  return `'${String(value).replace(/'/g, `'\\\\''`)}'`;\n}\n\nfunction formatCommand(program, args) {\n  return [program, ...args.map(shellQuote)].join(' ');\n}\n","sourceCodeStart":1,"sourceCodeEnd":36,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/lib/tmux-worktree-orchestrator.js#L1-L36","documentation":"Thrown by renderTemplate() in the tmux-worktree-orchestrator when the launcherCommand template (after trimming) is not a non-empty string. The orchestrator builds the per-worker launch command by interpolating variables into this template, so an empty/missing template means there is nothing to send to the tmux pane. The message is intentionally specific to the launcherCommand field rather than generic.","triggerScenarios":"buildOrchestrationPlan is called with config.launcherCommand = '' and a worker whose own launcherCommand is also empty or whitespace-only; launcherCommand is undefined but coerced to '' via the || '' fallback at line 187; renderTemplate is called directly with a non-string argument.","commonSituations":"A config file omits launcherCommand expecting a default to exist (there is none — empty string is the default); a CLI flag --launcher is provided but empty; a YAML config has launcherCommand: with no value (parsed as empty string); the orchestrator is driven programmatically without setting the global default.","solutions":["Set config.launcherCommand to a non-empty command, e.g. 'claude --resume {session_name}' or 'codex exec {task_file}'.","Set worker.launcherCommand on each worker object if you do not want a global default.","Validate the config upstream: if (!config.launcherCommand?.trim() && !workers.every(w => w.launcherCommand?.trim())) throw new Error('missing launcher');","Use one of the documented template variables ({task_file}, {session_name}, {worktree_path}, etc.) so the rendered command is meaningful."],"exampleFix":"// before\nbuildOrchestrationPlan({ repoRoot, workers: [{ task: 'fix bug' }] });\n// -> launcherCommand must be a non-empty string\n\n// after\nbuildOrchestrationPlan({\n  repoRoot,\n  launcherCommand: 'claude \"{task_file}\"',\n  workers: [{ task: 'fix bug' }],\n});","handlingStrategy":"validation","validationCode":"function resolveLauncher(config, worker) {\n  const cmd = (worker?.launcherCommand || config?.launcherCommand || '').trim();\n  return cmd.length > 0 ? cmd : null;\n}\n\nconst launcher = resolveLauncher(config, worker);\nif (!launcher) {\n  throw new Error('No launcherCommand configured for worker ' + (worker?.name || '?'));\n}","typeGuard":"function isNonEmptyString(value) {\n  return typeof value === 'string' && value.trim().length > 0;\n}","tryCatchPattern":"try {\n  buildOrchestrationPlan(config);\n} catch (error) {\n  if (/launcherCommand must be a non-empty string/.test(error.message)) {\n    config.launcherCommand = DEFAULT_LAUNCHER;\n    buildOrchestrationPlan(config);\n    return;\n  }\n  throw error;\n}","preventionTips":["Always set config.launcherCommand unless every worker sets its own.","Validate the config object with a schema before passing it to buildOrchestrationPlan.","Treat empty launcherCommand as a hard configuration error at load time.","Document a canonical launcher string so users have a copy-pasteable starting point."],"tags":["orchestration","tmux","configuration","validation"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}