actualbudget/actual · error

Expected availableValue to be a number but got

Error message

Expected availableValue to be a number but got 

What it means

ToBudget reads the 'to budget' amount from the envelope budget sheet via useEnvelopeSheetValue. The hook may legitimately return a number or null (while loading/invalid), but any other type means the sheet cell held bad data. The component guards this invariant and throws `Expected availableValue to be a number but got <value>`. Note the empty message suffix here means the value stringified to empty (e.g. undefined coerced oddly or empty string).

Solutions

  1. Ensure the component only renders when the envelope sheet for the month is loaded (check isBudgetInitialized/loading state)
  2. Verify the sheetValue provider for envelopeBudget.toBudget always returns number | null
  3. Coerce defensively at the hook call site: `Number.isFinite(v) ? v : 0`
  4. Clear corrupted budget cache / re-sync the budget if stored sheet data is bad

Example fix

// before
const availableValue = useEnvelopeSheetValue({ name: envelopeBudget.toBudget, value: 0 });
// after
const raw = useEnvelopeSheetValue({ name: envelopeBudget.toBudget, value: 0 });
const availableValue = typeof raw === 'number' ? raw : null;
Defensive patterns

Strategy: type-guard

Validate before calling

const raw = useEnvelopeSheetValue({ name: envelopeBudget.toBudget, value: 0 });
if (raw !== null && typeof raw !== 'number') {
  console.warn('toBudget sheet value invalid, skipping render');
  return null;
}

Type guard

function isSheetNumber(v: unknown): v is number | null {
  return v === null || (typeof v === 'number' && Number.isFinite(v));
}

Try / catch

try {
  return <ToBudget month={month} />;
} catch (e) {
  if (String(e?.message).startsWith('Expected availableValue to be a number')) {
    return <div className="tnum">0</div>;
  }
  throw e;
}

Prevention

When it happens

Trigger: useEnvelopeSheetValue for envelopeBudget.toBudget returns a non-numeric, non-null value — typically undefined (hook not yet bound to a sheet / called outside sheet scope), an empty string cell, or a corrupted/unsynced sheet value.

Common situations: Rendering ToBudget before the budget sheet is initialized; month changed while the sheet value factory returns stale data; a bug in a custom sheet value provider returning a string; upgrading loot-core where sheet value typing changed.

Related errors


AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29). Data as JSON: /api/errors/0a2ac056795fa8cd. Report an issue: GitHub.

Appendix: source

Thrown at packages/desktop-client/src/components/budget/envelope/budgetsummary/ToBudget.tsx:50

}: ToBudgetProps) {
  const [menuStep, _setMenuStep] = useState<string>('actions');
  const triggerRef = useRef(null);
  const format = useFormat();

  const ref = useRef<HTMLSpanElement>(null);
  const setMenuStep = useCallback(
    (menu: string) => {
      if (menu) ref.current?.focus();
      _setMenuStep(menu);
    },
    [ref, _setMenuStep],
  );
  const availableValue = useEnvelopeSheetValue({
    name: envelopeBudget.toBudget,
    value: 0,
  });
  if (typeof availableValue !== 'number' && availableValue !== null) {
    throw new Error(
      'Expected availableValue to be a number but got ' + availableValue,
    );
  }

  const [menuOpen, setMenuOpen] = useState(false);
  const [position, setPosition] = useState({ crossOffset: 0, offset: 0 });
  const resetPosition = (crossOffset = 0, offset = 0) =>
    setPosition({ crossOffset, offset });

  const handleContextMenu = (e: MouseEvent) => {
    e.preventDefault();
    const rect = e.currentTarget.getBoundingClientRect();
    setPosition({
      crossOffset: e.clientX - rect.left,
      offset: e.clientY - rect.bottom,
    });
    setMenuOpen(true);
  };

View on GitHub (pinned to d4334cb6e6)