affaan-m/ECC · error

Invalid Claude target root: expected a non-empty string

Error message

Invalid Claude target root: expected a non-empty string

What it means

After validating the hooks config shape, materializeManagedHooks requires targetRoot — the directory into which plugin root placeholders are substituted — to be a non-empty string. A missing, empty, or non-string targetRoot would produce hook commands with an invalid install path, so it throws before any materialization occurs.

Solutions

  1. Pass the absolute target installation directory as targetRoot
  2. Assert the value before calling: if (!root) throw ... or use path.resolve(...)
  3. Check argument order: materializeManagedHooks(hooksConfig, targetRoot)
  4. If deriving from CLI flags, provide a default and validate non-empty

Example fix

// before
materializeManagedHooks(cfg, opts.target)
// after
const root = opts.target || process.cwd();
if (typeof root !== 'string' || root.length === 0) throw new Error('target root required');
materializeManagedHooks(cfg, root)
Defensive patterns

Strategy: validation

Validate before calling

if (typeof targetRoot !== 'string' || targetRoot.length === 0) throw new Error('targetRoot must be a non-empty string');

Type guard

const isNonEmptyString = (v) => typeof v === 'string' && v.length > 0;

Try / catch

try { return materializeManagedHooks(cfg, root); } catch (e) { if (e.message.includes('target root')) { root = path.resolve('install-target'); return materializeManagedHooks(cfg, root); } throw e; }

Prevention

When it happens

Trigger: Calling materializeManagedHooks (via hydrateRecordedOperations/managedHooks/materialize/command/materialized/runTests) with targetRoot = undefined, "", null, or a non-string value.

Common situations: An install script with an unset target directory option, empty string from a failed path join, or arguments swapped so the root lands in the wrong parameter.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/68f7e17d75564e50. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/install/claude-settings.js:302

          ...(typeof hook.command === 'string' || Array.isArray(hook.command)
            ? {
              command: Array.isArray(hook.command)
                ? hook.command.map(resolveCommand)
                : resolveCommand(hook.command),
            }
            : {}),
        })),
      })),
    ])
  );
}

function materializeManagedHooks(hooksConfig, targetRoot) {
  if (!isJsonObject(hooksConfig) || !isJsonObject(hooksConfig.hooks)) {
    throw new Error('Invalid hooks config: expected a JSON object with a hooks object');
  }
  if (!isNonEmptyString(targetRoot)) {
    throw new Error('Invalid Claude target root: expected a non-empty string');
  }
  return validateManagedHooks(resolveManagedHookCommands(
    replacePluginRootPlaceholders(hooksConfig.hooks, targetRoot),
    targetRoot
  ));
}

function parseSettings(rawSettings, label = 'Claude settings') {
  let settings;
  try {
    settings = JSON.parse(rawSettings);
  } catch (error) {
    throw new Error(`Failed to parse ${label}: ${error.message}`, { cause: error });
  }
  return validateSettings(settings, label);
}

function readSettings(settingsPath, fileSystem = fs) {

View on GitHub (pinned to 8321021c54)