jackwener/OpenCLI · error · Error
not a directory
Error message
not a directory
What it means
reserveAudioFile creates the output directory, then verifies it with statSync and asserts it is actually a directory before reserving a target file with a lock file. If the path exists but is not a directory (e.g. a regular file or symlink to a file), it throws Error('not a directory'), which the catch block re-wraps into CommandExecutionError('MiniMax music cannot reserve output file <target>: not a directory').
Source
Thrown at clis/minimax/utils.js:186
}
export function resolveOutputDir(value) {
const raw = String(value ?? '').trim();
if (!raw) return path.join(os.homedir(), 'Music', 'minimax');
if (raw === '~') return os.homedir();
if (raw.startsWith('~/')) return path.join(os.homedir(), raw.slice(2));
if (raw.startsWith('~')) throw new ArgumentError(`Unsupported home-directory path: ${raw}`);
return path.resolve(raw);
}
export function reserveAudioFile(dir, model, format, now = new Date()) {
const stamp = now.toISOString().replace(/[-:]/g, '').replace(/\.\d+Z$/, 'Z');
const target = path.join(dir, `${model}-${stamp}.${format}`);
const lock = `${target}.lock`;
const staging = `${target}.${process.pid}.${randomUUID()}.tmp`;
try {
fs.mkdirSync(dir, { recursive: true });
if (!fs.statSync(dir).isDirectory()) throw new Error('not a directory');
fs.accessSync(dir, fs.constants.W_OK);
if (fs.existsSync(target)) throw new Error('target already exists');
fs.writeFileSync(lock, String(process.pid), { flag: 'wx', mode: 0o600 });
if (fs.existsSync(target)) {
fs.rmSync(lock, { force: true });
throw new Error('target appeared during reservation');
}
return { target, lock, staging };
} catch (error) {
throw new CommandExecutionError(`MiniMax music cannot reserve output file ${target}: ${error?.message ?? error}`);
}
}
export function commitAudioFile(reservation, bytes) {
try {
fs.writeFileSync(reservation.staging, bytes, { flag: 'wx', mode: 0o600 });
// A same-filesystem hard link publishes the complete staging inode and
// fails if target already exists on POSIX and Windows alike.View on GitHub (pinned to 49907e53dc)
Solutions
- Inspect the path: run ls -la on the configured dir; if it is a file, remove or rename it (rm <path>) and re-run
- Point the output-dir config at a genuine directory path
- Check for symlinks: replace a symlink-to-file with a real directory (mkdir <path>)
- Verify permissions/mount state if the path is on a network or removable volume
Example fix
// before: 'minimax' exists as a regular file
reserveAudioFile('~/Music/minimax', model, 'wav'); // throws
// after (shell)
// rm ~/Music/minimax && mkdir -p ~/Music/minimax
reserveAudioFile('~/Music/minimax', model, 'wav'); Defensive patterns
Strategy: validation
Validate before calling
import * as fs from 'node:fs';
function assertUsableDir(dir) {
try {
const st = fs.statSync(dir);
if (!st.isDirectory()) throw new Error(`${dir} exists but is not a directory`);
} catch (e) {
if (e.code === 'ENOENT') return; // will be created
if (String(e.message).includes('not a directory')) throw e;
throw e;
}
}
// run assertUsableDir(dir) before reserveAudioFile Type guard
function isDirectoryPath(p) {
try { return fs.statSync(p).isDirectory(); } catch { return false; }
} Try / catch
try {
const r = reserveAudioFile(dir, model, format);
} catch (e) {
if (String(e.message).includes('not a directory')) {
fs.rmSync(dir, { force: true }); // remove stray file
fs.mkdirSync(dir, { recursive: true }); // recreate as directory
// then retry reserveAudioFile
} else throw e;
} Prevention
- ls -la the configured output dir once during setup to confirm it is a directory
- Never point output-dir at a file path or a symlink to a file
- Recreate stray files at that path as directories before running generation
- Include a preflight stat check in automated pipelines
When it happens
Trigger: Passing a dir argument that exists on disk as a regular file/symlink-to-file; or a path component where mkdirSync created something but statSync shows a non-directory inode.
Common situations: Config points the output dir at an existing file (e.g. a stale 'minimax' file left by a previous tool); a mount point replaced by a file; a broken symlink where a directory was expected.
Related errors
- Unsupported home-directory path: ${raw}
- target already exists
- output must be a directory path
- output path is not a safe directory: ${ancestor}
- output path is not a safe directory: ${resolved}
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/91c9da6ef01d66b3.
Report an issue: GitHub.