{"record":{"id":"389cd5031ae466af","repo":"jackwener/OpenCLI","slug":"could-not-read-midjourney-usage-snapshots-error","errorCode":null,"errorMessage":"Could not read Midjourney usage snapshots: ${errorMessage(error)}","messagePattern":"Could not read Midjourney usage snapshots: (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/midjourney/utils.js","lineNumber":837,"sourceCode":"    remainingCredits: numberOrNull(account?.total_credits ?? account?.credits_total),\n  };\n  try {\n    await fs.mkdir(MIDJOURNEY_SITE_DIR, { recursive: true });\n    await fs.appendFile(USAGE_SNAPSHOT_PATH, `${JSON.stringify(row)}\\n`, 'utf8');\n  } catch (error) {\n    // A local monitoring write must never turn a completed paid job into a\n    // reported command failure: that would invite an accidental paid retry.\n    log.warn(`Could not persist Midjourney quota snapshot: ${errorMessage(error)}`);\n  }\n  return row;\n}\n\nexport async function readQuotaTrend(account) {\n  let raw = '';\n  try {\n    raw = await fs.readFile(USAGE_SNAPSHOT_PATH, 'utf8');\n  } catch (error) {\n    if (error?.code !== 'ENOENT') throw new CommandExecutionError(`Could not read Midjourney usage snapshots: ${errorMessage(error)}`);\n  }\n  const currentStart = isoFromMillis(account?.billing_period?.start);\n  const rows = raw.split(/\\r?\\n/).filter(Boolean).flatMap((line) => {\n    try {\n      const parsed = JSON.parse(line);\n      return parsed?.billingStart === currentStart ? [parsed] : [];\n    } catch {\n      return [];\n    }\n  }).filter((row) => Number.isFinite(Date.parse(row.observedAt)) && Number.isFinite(Number(row.periodCreditsUsed)));\n  if (rows.length < 2) return { avgDailyMinutes: null, projectedExhaustionDate: null };\n  const first = rows[0];\n  const last = rows.at(-1);\n  const elapsedDays = (Date.parse(last.observedAt) - Date.parse(first.observedAt)) / 86_400_000;\n  const usedMinutes = creditsToFastMinutes(Number(last.periodCreditsUsed) - Number(first.periodCreditsUsed));\n  if (!(elapsedDays >= 1) || !(usedMinutes > 0)) return { avgDailyMinutes: null, projectedExhaustionDate: null };\n  const avgDailyMinutes = Number((usedMinutes / elapsedDays).toFixed(2));\n  const remainingMinutes = creditsToFastMinutes(account?.total_credits ?? account?.credits_total);","sourceCodeStart":819,"sourceCodeEnd":855,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/midjourney/utils.js#L819-L855","documentation":"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.","triggerScenarios":"readQuotaTrend calls fs.readFile(USAGE_SNAPSHOT_PATH) and the rejection has a code other than 'ENOENT' — e.g. EACCES, EISDIR, EMFILE, EIO.","commonSituations":"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.","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"],"exampleFix":"// before\nconst trend = await readQuotaTrend(account);\n// after\nlet trend = [];\ntry {\n  trend = await readQuotaTrend(account);\n} catch (err) {\n  log.warn(`Skipping quota trend: ${err.message}`);\n}","handlingStrategy":"fallback","validationCode":"async function snapshotReadable(p) {\n  try { await fs.promises.access(p, fs.constants.R_OK); return true; }\n  catch (e) { return e.code === 'ENOENT'; }\n}","typeGuard":"function isMissingFileError(err) {\n  return err?.code === 'ENOENT';\n}","tryCatchPattern":"let trend = [];\ntry {\n  trend = await readQuotaTrend(account);\n} catch (err) {\n  log.warn(`Quota trend unavailable: ${err.message}`); // degrade gracefully\n}","preventionTips":["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"],"tags":["filesystem","file-read","permissions","midjourney"],"backgroundTag":"file-read-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}