affaan-m/ECC · error · Error

ECC_PROJECT_DIR must be an absolute path within /workspace.

Error message

ECC_PROJECT_DIR must be an absolute path within /workspace.

What it means

The launch() function in the terminal-opener script probes whether the configured terminal (WezTerm) is installed and responsive before spawning it, by running `<terminal> --version`. If detectTerminalCapability() returns available=false (binary missing, probe threw, probe timed out with ETIMEDOUT, or the version probe exited non-zero), launch() throws a combined 'reason: action' message. The action field always tells you to install WezTerm and ensure it is on PATH.

Source

Thrown at docker/plugin-setup/resolve-project-dir.js:15

#!/usr/bin/env node

'use strict';

const path = require('path');

const WORKSPACE_ROOT = '/workspace';

function resolveProjectDir(candidate) {
  if (
    typeof candidate !== 'string'
    || !path.posix.isAbsolute(candidate)
    || /[\0\r\n]/.test(candidate)
  ) {
    throw new Error('ECC_PROJECT_DIR must be an absolute path within /workspace.');
  }

  const resolved = path.posix.resolve(candidate);
  if (resolved === WORKSPACE_ROOT || !resolved.startsWith(`${WORKSPACE_ROOT}/`)) {
    throw new Error('ECC_PROJECT_DIR must be a child path within /workspace.');
  }
  return resolved;
}

function main() {
  try {
    process.stdout.write(`${resolveProjectDir(process.argv[2])}\n`);
  } catch (error) {
    process.stderr.write(`Error: ${error.message}\n`);
    process.exitCode = 2;
  }
}

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Install WezTerm (e.g. brew install --cask wezterm on macOS, or the official package for your OS) and confirm `wezterm --version` works in the same shell that runs this script.
  2. Ensure the wezterm binary directory is on PATH for the Node process — check process.env.PATH inside the script and fix the launching shell/IDE environment.
  3. Call detectTerminalCapability(plan) yourself before launch() and branch on the returned reason/detail instead of letting launch() throw.
  4. If you injected a custom spawnSync via dependencies, verify your stub returns { status: 0, stdout: '...' } rather than erroring.

Example fix

// before
launch(plan); // throws if wezterm missing

// after
const cap = detectTerminalCapability(plan);
if (!cap.available) {
  console.error(`Cannot launch: ${cap.reason} (${cap.detail}). ${cap.action}`);
  process.exit(1);
}
const result = launch(plan);
Defensive patterns

Strategy: validation

Validate before calling

// Call detectTerminalCapability yourself before launch() and branch on the result.
const cap = detectTerminalCapability(plan);
if (!cap.available) {
  console.error(`Terminal unavailable: ${cap.reason} — ${cap.detail}`);
  console.error(`Action: ${cap.action}`);
  process.exit(1);
}
const result = launch(plan);

Type guard

// Narrow a capability object before relying on it.
function isCapable(c) {
  return c && c.available === true && typeof c.version === 'string';
}
if (!isCapable(cap)) { /* handle */ }

Try / catch

try {
  const result = launch(plan);
} catch (e) {
  // e.message is `${reason}: ${action}` — surface both to the user.
  console.error(`Terminal launch failed: ${e.message}`);
  process.exitCode = 1;
}

Prevention

When it happens

Trigger: Calling launch(plan) when (a) plan.ok is false — the plan itself is invalid; (b) the wezterm binary is not on PATH so spawnSync throws or returns ENOENT (reason='not-installed'); (c) `wezterm --version` times out after SYNC_TIMEOUT_MS (reason='probe-failed'); (d) `wezterm --version` exits with a non-zero status (reason='probe-failed').

Common situations: WezTerm is not installed on a headless/CI machine; wezterm is installed but not on the PATH inherited by the Node process; a shell misconfiguration means the probe cannot find the binary; WezTerm is installed but crashes on `--version` due to a broken config or missing display.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/bc6daa1c9e6aa703. Report an issue: GitHub.