bmad-code-org/BMAD-METHOD · error · TypeError
Path must be a string.
Error message
Path must be a string.
What it means
Thrown by expandUserPath (as TypeError) when inputPath is not a string. The method does inputPath.trim() and tilde expansion, both of which require a string; non-string input is rejected at the top before any path logic runs.
Source
Thrown at tools/installer/ui.js:1659
// Stop at root
const parent = path.dirname(currentPath);
if (await fs.pathExists(parent)) {
return parent;
}
currentPath = parent;
}
return null; // No existing parent found (shouldn't happen in practice)
}
/**
* Expands the user-provided path: handles ~ and resolves to absolute.
* @param {string} inputPath - User input path.
* @returns {string} Absolute expanded path.
*/
expandUserPath(inputPath) {
if (typeof inputPath !== 'string') {
throw new TypeError('Path must be a string.');
}
let expanded = inputPath.trim();
// Handle tilde expansion
if (expanded.startsWith('~')) {
if (expanded === '~') {
expanded = os.homedir();
} else if (expanded.startsWith('~' + path.sep)) {
const pathAfterHome = expanded.slice(2); // Remove ~/ or ~\
expanded = path.join(os.homedir(), pathAfterHome);
} else {
const restOfPath = expanded.slice(1);
const separatorIndex = restOfPath.indexOf(path.sep);
const username = separatorIndex === -1 ? restOfPath : restOfPath.slice(0, separatorIndex);
if (username) {
throw new Error(`Path expansion for ~${username} is not supported. Please use an absolute path or ~${path.sep}`);
}View on GitHub (pinned to b70486b9bd)
Solutions
- Ensure the value passed is a string before calling expandUserPath.
- Guard callers: if (typeof dir === 'string') expandUserPath(dir).
- Default options.directory to undefined and skip the directory branch when unset, rather than forwarding undefined.
Example fix
// before
// const dir = this.expandUserPath(options.directory); // options.directory may be undefined
//
// after
// if (typeof options.directory !== 'string') {
// throw new Error('options.directory must be a string');
// }
// const dir = this.expandUserPath(options.directory); Defensive patterns
Strategy: type-guard
Validate before calling
if (options.directory != null && typeof options.directory !== 'string') {
throw new TypeError('options.directory must be a string');
}
if (typeof options.directory === 'string') {
const dir = ui.expandUserPath(options.directory);
} Type guard
function isStringPath(p) { return typeof p === 'string'; } Try / catch
try {
dir = ui.expandUserPath(options.directory);
} catch (e) {
if (/Path must be a string/.test(e.message)) {
if (typeof options.directory !== 'string') options.directory = undefined;
} else { throw e; }
} Prevention
- Type-check CLI options at the boundary before forwarding them to expandUserPath.
- Default optional path options to undefined and branch on typeof === 'string'.
When it happens
Trigger: Calling expandUserPath(undefined), expandUserPath(null), or passing a number/object. Indirectly when options.directory is truthy but not a string and is forwarded here.
Common situations: Programmatic calls passing undefined; a CLI option parsed in a way that yields a non-string; config loading returning undefined that isn't defaulted.
Related errors
- Path expansion for ~${username} is not supported. Please use
- version is not available when nothing is installed
- ${label} does not exist: ${dirPath}
- ${label} is not a directory: ${dirPath}
- ${label} is not readable: ${dirPath}
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/bf05557c43f72575.
Report an issue: GitHub.