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

  1. Run ls -la on USAGE_SNAPSHOT_PATH and fix permissions/ownership (chmod/chown) so the current user can read it
  2. If the path is a directory or corrupted, delete or restore it — the CLI recreates snapshots on next use
  3. Close other processes holding file descriptors or raise the ulimit for EMFILE
  4. Check underlying storage health if EIO persists on network mounts
  5. 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

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


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/389cd5031ae466af. Report an issue: GitHub.