bmad-code-org/BMAD-METHOD · error · Error
Invalid directory: ${validation}
Error message
Invalid directory: ${validation} What it means
Thrown from the directory step when validateDirectorySync(expandedDir) returns a non-undefined error string. validateDirectorySync returns messages for: path exists but is not a directory, directory not writable, path doesn't exist and can't be created (parent not writable or no existing parent), or expansion errors re-thrown from expandUserPath (e.g. ~user syntax).
Source
Thrown at tools/installer/ui.js:252
semver.prerelease(installerPackageJson.version) !== null &&
!channelOptions.global &&
channelOptions.nextSet.size === 0 &&
channelOptions.pins.size === 0
) {
channelOptions.global = 'next';
await prompts.log.info(
'Launched from a prerelease — installing all external modules from main HEAD (next channel). Pass --all-stable or --pin to override.',
);
}
// Get directory from options or prompt
let confirmedDirectory;
if (options.directory) {
// Use provided directory from command-line
const expandedDir = this.expandUserPath(options.directory);
const validation = this.validateDirectorySync(expandedDir);
if (validation) {
throw new Error(`Invalid directory: ${validation}`);
}
confirmedDirectory = expandedDir;
await prompts.log.info(`Using directory from command-line: ${confirmedDirectory}`);
} else {
confirmedDirectory = await this.getConfirmedDirectory();
}
const { Installer } = require('./core/installer');
const installer = new Installer();
const { bmadDir } = await installer.findBmadDir(confirmedDirectory);
// Check if there's an existing BMAD installation
const hasExistingInstall = await fs.pathExists(bmadDir);
// Track action type (only set if there's an existing installation)
let actionType;
// Only show action menu if there's an existing installationView on GitHub (pinned to b70486b9bd)
Solutions
- Pass a directory that exists and is writable, or whose nearest existing parent is writable so the installer can create it.
- Fix permissions on the target or parent: chmod +w <parent> / chown.
- Use an absolute path or a ~/subpath form this installer supports; avoid ~user syntax.
- Run the installer with adequate privileges for the chosen install location.
Example fix
# before # --directory /opt/readonly/bmad # parent not writable # --directory ~/somefile # exists but is a file # # after # --directory ~/projects/my-bmad # parent (~) is writable # sudo chmod +w /opt/readonly && --directory /opt/readonly/bmad
Defensive patterns
Strategy: validation
Validate before calling
const dir = ui.expandUserPath(options.directory);
const err = ui.validateDirectorySync(dir);
if (err) {
console.error(`Directory '${dir}' is invalid: ${err}. Choose a writable path.`);
process.exit(1);
} Type guard
function isStringPath(p) { return typeof p === 'string' && p.length > 0; } Try / catch
try {
await ui.run(options);
} catch (e) {
if (/^Invalid directory:/.test(e.message)) {
// re-prompt for a writable directory, or fix permissions and retry
} else { throw e; }
} Prevention
- Pre-validate --directory with validateDirectorySync before launching the full flow.
- Prefer a writable home-relative path; avoid system directories without elevated privileges.
- Don't use ~user syntax — expand to an absolute path first.
When it happens
Trigger: Passing --directory <path> where the path doesn't exist and its nearest existing parent isn't writable; exists but is a file; exists but lacks write permission; or fails tilde expansion (~user syntax).
Common situations: Wrong --directory argument; read-only filesystem; pointing at a file; permission denied; using ~otheruser which isn't supported.
Related errors
- ${label} is not readable: ${dirPath}
- ${label} is not readable: ${filePath}
- ${label}: permission denied creating directory: ${dirPath}
- ${label} is not writable: ${dirPath}
- ${label} does not exist: ${dirPath}
AI-assisted analysis of bmad-code-org/BMAD-METHOD@b70486b9bd (2026-08-13).
Data as JSON: /api/errors/a39667b658d954e0.
Report an issue: GitHub.