we-promise/sure · warning · Assistant::Error
Category '#{ref}' not found. Use get_categories to list cate
Error message
Category '#{ref}' not found. Use get_categories to list categories. What it means
Raised by find_budget_category! when no category of the family matches the reference: the UUID branch (valid_uuid? then family.categories.find_by(id:)) and the case-insensitive exact-name branch (LOWER(name) = ref.downcase) both miss. The message points the caller at get_categories because the resolution requires an exact name or the real id.
Source
Thrown at app/models/assistant/function/update_budget.rb:167
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
- Call get_categories (or get_budget) first and pass back the exact name or id it returned — that round-trip is the intended flow.
- If the category genuinely should exist, create it via the normal category API before budgeting it.
- Check for renames/deletions if a previously working name suddenly fails.
- Never cross-pollinate ids between families — the lookup is scoped to family.categories.
Example fix
# before
update_budget.call({ "categories" => [{ "category" => "Food", "amount" => 500 }] })
# after — resolve the real name/id first
cats = get_categories.call({}) # => [{ "name" => "Groceries", "id" => "0198..." }], ...
update_budget.call({ "categories" => [{ "category" => cats.first["id"], "amount" => 500 }] }) Defensive patterns
Strategy: validation
Validate before calling
def resolvable_category?(family, ref)
return true if family.categories.exists?(id: ref)
family.categories.exists?("LOWER(name) = ?", ref.to_s.downcase)
end Type guard
# Ruby
def existing_category(family, ref)
family.categories.find_by(id: ref) || family.categories.where("LOWER(name) = ?", ref.to_s.downcase).first # nil when not found
end Prevention
- Always call get_categories/get_budget first and pass back names/ids verbatim — no synonyms, no plurals, no partial matches.
- Scope lookups to family.categories; ids from another family will never resolve.
- Watch for concurrent renames/deletes if a previously working name starts failing.
When it happens
Trigger: update_budget with category: "Food & Dining" when the family's category is named "Groceries"; a category id from another family (UUID parses but find_by scopes to family.categories); a typo'd or translated name; a category that was renamed or deleted between get_budget and update_budget.
Common situations: LLM hallucinating plausible category names instead of reading get_categories/get_budget output first; concurrent renames; case variants are fine (matching is case-insensitive) but synonyms, plurals, and partial matches are not ("Grocer" fails).
Related errors
- #{label} must be a non-negative number.
- Each categories entry needs a category name or id.
- '#{ref}' is the unallocated remainder of budgeted_spending a
- Invalid month: #{raw}. Use YYYY-MM or MMM-YYYY.
- 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/1395264a65d9c2d3.
Report an issue: GitHub.