koala73/worldmonitor · error · Error

Umami volume is not ready: ${volume.status ?? 'unknown'}

Error message

Umami volume is not ready: ${volume.status ?? 'unknown'}

What it means

evaluateUmamiStorage computes usage trends and projections for an Umami volume, but only makes sense when the volume is in the 'Ready' state. It throws when volume.status is anything else (or undefined, rendered as 'unknown'), because size/current-size samples for a non-Ready volume would produce misleading projections. The check runs after validating the timestamp, sizeMB, and currentSizeMB inputs.

Solutions

  1. Wait for the volume to reach status 'Ready' (poll or re-run the check later) before evaluating storage.
  2. If the volume is stuck non-Ready, investigate the provider volume state (attach errors, provisioning failures) and repair it.
  3. If status is missing from your data source, fix the scrape/mapping so volume.status is populated, then re-run.

Example fix

// before
evaluateUmamiStorage({ volume: { sizeMB: 1024, currentSizeMB: 512 }, samples, now });
// after
if (volume.status === 'Ready') {
  evaluateUmamiStorage({ volume, samples, now });
}
Defensive patterns

Strategy: validation

Validate before calling

if (volume?.status !== 'Ready') {
  throw new Error(`defer storage evaluation; volume status=${volume?.status ?? 'unknown'}`);
}
evaluateUmamiStorage({ volume, samples, now });

Type guard

function isReadyVolume(volume) {
  return typeof volume === 'object' && volume !== null && volume.status === 'Ready'
    && typeof volume.sizeMB === 'number' && volume.sizeMB > 0
    && typeof volume.currentSizeMB === 'number' && volume.currentSizeMB >= 0;
}

Try / catch

try {
  const result = evaluateUmamiStorage({ volume, samples, now });
} catch (e) {
  if (e.message.startsWith('Umami volume is not ready')) scheduleRecheck(volume);
  else throw e;
}

Prevention

When it happens

Trigger: Passing a volume object whose status is e.g. 'Pending', 'Attaching', 'Failed', or missing entirely, while still supplying valid sizeMB and currentSizeMB values.

Common situations: Running the storage check immediately after resizing/creating a volume before it finished provisioning, checking a volume stuck in a failed attach state, or scraping status from an API that omits the field.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of koala73/worldmonitor@7d06c8633d (2026-09-15). Data as JSON: /api/errors/6bba888cc3e1ace6. Report an issue: GitHub.

Appendix: source

Thrown at scripts/check-umami-storage.mjs:119

  const identity = volumeIdentity(volume);
  if (identity === null) throw new Error('Umami volume must have a stable identity');

  const priorCapacityMB = finiteNonNegative(previousState?.capacityMB);
  const sameVolume = previousState?.volumeIdentity === identity && priorCapacityMB === capacityMB;
  const samples = (sameVolume ? normalizeSamples(previousState?.samples, nowMs) : [])
    .filter((sample) => Date.parse(sample.sampledAt) !== nowMs);
  samples.push({ sampledAt: new Date(nowMs).toISOString(), currentSizeMB });
  return { version: 1, volumeIdentity: identity, capacityMB, samples };
}

export function evaluateUmamiStorage({ volume, samples = [], now = Date.now() }) {
  const nowMs = timestampMs(now);
  const capacityMB = finiteNonNegative(volume?.sizeMB);
  const currentSizeMB = finiteNonNegative(volume?.currentSizeMB);
  if (nowMs === null) throw new Error('Storage evaluation time must be a valid timestamp');
  if (capacityMB === null || capacityMB <= 0) throw new Error('Umami volume sizeMB must be greater than zero');
  if (currentSizeMB === null) throw new Error('Umami volume currentSizeMB must be a non-negative number');
  if (volume.status !== 'Ready') throw new Error(`Umami volume is not ready: ${volume.status ?? 'unknown'}`);

  const usageRatio = currentSizeMB / capacityMB;
  const points = normalizeSamples(samples, nowMs)
    .filter((sample) => Date.parse(sample.sampledAt) !== nowMs)
    .map((sample) => ({ day: (Date.parse(sample.sampledAt) - nowMs) / DAY_MS, sizeMB: sample.currentSizeMB }));
  points.push({ day: 0, sizeMB: currentSizeMB });

  let growthMBPerDay = null;
  let projectedHeadroomDays = null;
  if (-points[0].day >= UMAMI_STORAGE_POLICY.minimumTrendSpanDays) {
    const meanDay = points.reduce((sum, point) => sum + point.day, 0) / points.length;
    const meanSizeMB = points.reduce((sum, point) => sum + point.sizeMB, 0) / points.length;
    let covariance = 0;
    let variance = 0;
    for (const point of points) {
      covariance += (point.day - meanDay) * (point.sizeMB - meanSizeMB);
      variance += (point.day - meanDay) ** 2;
    }

View on GitHub (pinned to 7d06c8633d)