we-promise/sure · warning · Assistant::Error
'#{ref}' is the unallocated remainder of budgeted_spending a
Error message
'#{ref}' is the unallocated remainder of budgeted_spending and cannot be set directly. Adjust budgeted_spending or category amounts instead. What it means
Raised by find_budget_category! when the requested category reference case-insensitively matches one of Category.all_uncategorized_names. "Uncategorized" is not a stored category: it is the derived remainder of budget.budgeted_spending minus the sum of per-category allocations, so writing it directly is refused to keep the invariant that it equals the remainder.
Source
Thrown at app/models/assistant/function/update_budget.rb:165
private
def parse_amount!(raw, label)
value = Float(raw)
raise Assistant::Error, "#{label} must be a non-negative number." if !value.finite? || value.negative?
value
rescue ArgumentError, TypeError
raise Assistant::Error, "#{label} must be a non-negative number."
end
def find_budget_category!(budget, ref)
ref = ref.to_s.strip
raise Assistant::Error, "Each categories entry needs a category name or id." if ref.blank?
category = valid_uuid?(ref) ? family.categories.find_by(id: ref) : nil
category ||= family.categories.where("LOWER(name) = ?", ref.downcase).first
if category.nil?
if Category.all_uncategorized_names.any? { |name| name.casecmp?(ref) }
raise Assistant::Error, "'#{ref}' is the unallocated remainder of budgeted_spending and cannot be set directly. Adjust budgeted_spending or category amounts instead."
end
raise Assistant::Error, "Category '#{ref}' not found. Use get_categories to list categories."
end
budget.budget_categories.find_by(category_id: category.id) ||
raise(Assistant::Error, "No budget row exists for category '#{category.name}' in #{budget.to_param}.")
end
def format_money(value)
Money.new(value || 0, family.currency).format
end
def error(key, message)
{ success: false, error: key, message: message }
end
end
View on GitHub (pinned to e69894adb9)
Solutions
- To shrink the leftover, lower budgeted_spending: update_budget({ month: ..., budgeted_spending: <smaller total> }).
- To allocate the leftover, raise amounts on real categories (or create them first) — their sum then consumes the remainder.
- Tell the model up front (the tool description already states it) that the Uncategorized bucket is derived and not directly settable.
Example fix
# before
update_budget.call({ "categories" => [{ "category" => "Uncategorized", "amount" => 500 }] })
# after — grow a real category and/or shrink the total instead
update_budget.call({
"budgeted_spending" => 6000,
"categories" => [{ "category" => "Groceries", "amount" => 900 }]
}) Defensive patterns
Strategy: validation
Validate before calling
reserved = Category.all_uncategorized_names.map(&:downcase) if reserved.include?(ref.to_s.downcase) raise ArgumentError, "uncategorized is derived — adjust budgeted_spending or real categories" end
Type guard
# Ruby
def uncategorized_ref?(ref)
Category.all_uncategorized_names.any? { |name| name.casecmp?(ref.to_s) }
end Prevention
- Route 'allocate the leftover' requests to budgeted_spending or real categories, never to the Uncategorized bucket.
- Teach the model the invariant early: the tool description already states the bucket is the unallocated remainder of budgeted_spending.
- Check available_to_allocate from get_budget output to decide whether to raise category sums or lower the total.
When it happens
Trigger: update_budget with categories: [{ "category" => "Uncategorized", "amount" => 500 }] (any case — the check uses casecmp?) reaches app/models/assistant/function/update_budget.rb:164-166 because no real category matched the name, but a reserved uncategorized name did.
Common situations: LLM or user trying to earmark the leftover bucket; families whose budgeted_spending exceeds sum of category amounts so the model 'helpfully' allocates the difference to Uncategorized; localized uncategorized names being matched through all_uncategorized_names.
Related errors
- #{label} must be a non-negative number.
- Each categories entry needs a category name or id.
- Invalid month: #{raw}. Use YYYY-MM or MMM-YYYY.
- Category '#{ref}' not found. Use get_categories to list cate
- Assistant exceeded the tool-call limit of #{max_tool_call_it
AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21).
Data as JSON: /api/errors/f727a7ec6133d322.
Report an issue: GitHub.