jackwener/OpenCLI · error · CommandExecutionError
Could not read Midjourney usage snapshots: ${errorMessage(er
Error message
Could not read Midjourney usage snapshots: ${errorMessage(error)} What it means
A CommandExecutionError raised when reading the usage-snapshot JSONL file fails for any reason other than ENOENT. A missing file is tolerated (returns empty trend), but permission errors, I/O failures, or corrupt-device errors are wrapped and rethrown because the quota trend cannot be computed.
Source
Thrown at clis/midjourney/utils.js:837
remainingCredits: numberOrNull(account?.total_credits ?? account?.credits_total),
};
try {
await fs.mkdir(MIDJOURNEY_SITE_DIR, { recursive: true });
await fs.appendFile(USAGE_SNAPSHOT_PATH, `${JSON.stringify(row)}\n`, 'utf8');
} catch (error) {
// A local monitoring write must never turn a completed paid job into a
// reported command failure: that would invite an accidental paid retry.
log.warn(`Could not persist Midjourney quota snapshot: ${errorMessage(error)}`);
}
return row;
}
export async function readQuotaTrend(account) {
let raw = '';
try {
raw = await fs.readFile(USAGE_SNAPSHOT_PATH, 'utf8');
} catch (error) {
if (error?.code !== 'ENOENT') throw new CommandExecutionError(`Could not read Midjourney usage snapshots: ${errorMessage(error)}`);
}
const currentStart = isoFromMillis(account?.billing_period?.start);
const rows = raw.split(/\r?\n/).filter(Boolean).flatMap((line) => {
try {
const parsed = JSON.parse(line);
return parsed?.billingStart === currentStart ? [parsed] : [];
} catch {
return [];
}
}).filter((row) => Number.isFinite(Date.parse(row.observedAt)) && Number.isFinite(Number(row.periodCreditsUsed)));
if (rows.length < 2) return { avgDailyMinutes: null, projectedExhaustionDate: null };
const first = rows[0];
const last = rows.at(-1);
const elapsedDays = (Date.parse(last.observedAt) - Date.parse(first.observedAt)) / 86_400_000;
const usedMinutes = creditsToFastMinutes(Number(last.periodCreditsUsed) - Number(first.periodCreditsUsed));
if (!(elapsedDays >= 1) || !(usedMinutes > 0)) return { avgDailyMinutes: null, projectedExhaustionDate: null };
const avgDailyMinutes = Number((usedMinutes / elapsedDays).toFixed(2));
const remainingMinutes = creditsToFastMinutes(account?.total_credits ?? account?.credits_total);View on GitHub (pinned to 49907e53dc)
Solutions
- Run ls -la on USAGE_SNAPSHOT_PATH and fix permissions/ownership (chmod/chown) so the current user can read it
- If the path is a directory or corrupted, delete or restore it — the CLI recreates snapshots on next use
- Close other processes holding file descriptors or raise the ulimit for EMFILE
- Check underlying storage health if EIO persists on network mounts
- If you only need best-effort trend data, catch this error at the call site and proceed without the trend
Example fix
// before
const trend = await readQuotaTrend(account);
// after
let trend = [];
try {
trend = await readQuotaTrend(account);
} catch (err) {
log.warn(`Skipping quota trend: ${err.message}`);
} Defensive patterns
Strategy: fallback
Validate before calling
async function snapshotReadable(p) {
try { await fs.promises.access(p, fs.constants.R_OK); return true; }
catch (e) { return e.code === 'ENOENT'; }
} Type guard
function isMissingFileError(err) {
return err?.code === 'ENOENT';
} Try / catch
let trend = [];
try {
trend = await readQuotaTrend(account);
} catch (err) {
log.warn(`Quota trend unavailable: ${err.message}`); // degrade gracefully
} Prevention
- Run the CLI consistently as the same user that wrote the snapshots
- Keep USAGE_SNAPSHOT_PATH on local (not NFS/network) storage
- Avoid external processes chmod-ing or replacing the snapshot file/directory
- Treat the trend as optional data and wrap calls in a safe helper
When it happens
Trigger: readQuotaTrend calls fs.readFile(USAGE_SNAPSHOT_PATH) and the rejection has a code other than 'ENOENT' — e.g. EACCES, EISDIR, EMFILE, EIO.
Common situations: Another process holds the snapshot file with restrictive permissions; the snapshot path became a directory; running the CLI as a different user than the one that wrote the snapshots; too many open file descriptors (EMFILE) under heavy parallelism; NFS/network-fs I/O errors.
Related errors
- Could not store Midjourney ${kind} at ${filePath}: ${errorMe
- File could not be read: ${path}
- Receipt file cannot be read: ${receipt}
- Could not write Midjourney image ${filePath}: ${errorMessage
- Could not write Midjourney media ${filePath}: ${errorMessage
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/389cd5031ae466af.
Report an issue: GitHub.