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
- Pass the absolute target installation directory as targetRoot
- Assert the value before calling: if (!root) throw ... or use path.resolve(...)
- Check argument order: materializeManagedHooks(hooksConfig, targetRoot)
- 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
- Resolve and assert the target directory before install calls
- Never pass empty-string CLI option defaults straight through
- Fix argument order with a named-options object where possible
- Log targetRoot at debug level during installs
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
- application config exceeds local size limit
- Asset request_id/modality does not match the bundle
- at least one genre spec is required
- bundle collections must be lists
- candidate configuration exceeds 1 MiB
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)