{"record":{"id":"b9a030461e391bd4","repo":"jackwener/OpenCLI","slug":"minimax-music-cannot-reserve-output-file-target","errorCode":null,"errorMessage":"MiniMax music cannot reserve output file ${target}: ${error?.message ?? error}","messagePattern":"MiniMax music cannot reserve output file (.+?): (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/minimax/utils.js","lineNumber":196,"sourceCode":"\nexport function reserveAudioFile(dir, model, format, now = new Date()) {\n    const stamp = now.toISOString().replace(/[-:]/g, '').replace(/\\.\\d+Z$/, 'Z');\n    const target = path.join(dir, `${model}-${stamp}.${format}`);\n    const lock = `${target}.lock`;\n    const staging = `${target}.${process.pid}.${randomUUID()}.tmp`;\n    try {\n        fs.mkdirSync(dir, { recursive: true });\n        if (!fs.statSync(dir).isDirectory()) throw new Error('not a directory');\n        fs.accessSync(dir, fs.constants.W_OK);\n        if (fs.existsSync(target)) throw new Error('target already exists');\n        fs.writeFileSync(lock, String(process.pid), { flag: 'wx', mode: 0o600 });\n        if (fs.existsSync(target)) {\n            fs.rmSync(lock, { force: true });\n            throw new Error('target appeared during reservation');\n        }\n        return { target, lock, staging };\n    } catch (error) {\n        throw new CommandExecutionError(`MiniMax music cannot reserve output file ${target}: ${error?.message ?? error}`);\n    }\n}\n\nexport function commitAudioFile(reservation, bytes) {\n    try {\n        fs.writeFileSync(reservation.staging, bytes, { flag: 'wx', mode: 0o600 });\n        // A same-filesystem hard link publishes the complete staging inode and\n        // fails if target already exists on POSIX and Windows alike.\n        fs.linkSync(reservation.staging, reservation.target);\n        cleanupAudioFile(reservation);\n        return reservation.target;\n    } catch (error) {\n        cleanupAudioFile(reservation);\n        throw new CommandExecutionError(`MiniMax music could not atomically write ${reservation.target}: ${error?.message ?? error}`);\n    }\n}\n\nexport function cleanupAudioFile(reservation) {","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/minimax/utils.js#L178-L214","documentation":"reserveAudioFile wraps every failure of the reservation sequence (mkdir, stat, W_OK access check, exclusive lock creation, TOCTOU re-check) in a CommandExecutionError: 'MiniMax music cannot reserve output file <target>: <cause>'. The cause string is appended via error?.message ?? error, so the root reason always appears after the colon.","triggerScenarios":"Any failure inside reserveAudioFile's try block: the --output path is not a directory, the directory is not writable (fs.accessSync W_OK fails), fs.writeFileSync with flag 'wx' fails because <target>.lock already exists (stale lock from a crashed run), or the target appeared during the reservation race.","commonSituations":"--output pointing to an existing regular file instead of a directory; read-only or permission-restricted output directories (restricted CI workspace, ~/Music/minimax owned by another user); leftover .lock files from killed commands making 'wx' creation throw EEXIST.","solutions":["Read the cause after the colon: if it mentions EEXIST/.lock, delete the stale `<target>.lock` file and retry.","If the cause is 'not a directory' or permission denied, pass a --output path that is an existing writable directory.","Verify write access: `touch <dir>/.writetest`, then fix ownership/ACLs if it fails.","If two jobs collide concurrently, serialize them or use separate output directories."],"exampleFix":"// before\n$ minimax music --output /etc  # not writable\nError: MiniMax music cannot reserve output file /etc/minimax-music-...: EACCES\n\n// after\n$ minimax music --output ~/Music/minimax","handlingStrategy":"validation","validationCode":"import fs from 'node:fs';\nfunction ensureWritableOutputDir(dir) {\n  const resolved = path.resolve(String(dir ?? '').replace(/^~(?=$|\\/)/, os.homedir()));\n  fs.mkdirSync(resolved, { recursive: true });\n  if (!fs.statSync(resolved).isDirectory()) throw new Error(`not a directory: ${resolved}`);\n  fs.accessSync(resolved, fs.constants.W_OK);\n  return resolved;\n}","typeGuard":null,"tryCatchPattern":"try {\n  runMinimaxMusic();\n} catch (e) {\n  const m = /cannot reserve output file .*: (.+)$/.exec(e.message);\n  if (m && m[1].includes('EEXIST')) {\n    fs.rmSync(`${target}.lock`, { force: true }); // stale lock from crashed run\n  } else if (m && /EACCES|not a directory/.test(m[1])) {\n    console.error('Fix --output: must be a writable directory');\n  } else { throw e; }\n}","preventionTips":["Always pass --output as an existing writable directory, not a file.","Verify write access with `touch <dir>/.writetest` before long-running jobs.","Clean up stale <target>.lock files after killing a run.","Avoid restricted paths like /usr, /etc, or read-only CI mounts as output."],"tags":["filesystem","permissions","cli","lock-file"],"backgroundTag":"file-write-permission-denied","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}