we-promise/sure · error · Provider::Openai::Error

No categories available for auto-categorization

Error message

No categories available for auto-categorization

What it means

Raised by Provider::Openai#auto_categorize when the user_categories array is blank. The LLM prompt needs the family's category list to classify transactions against; with zero categories there is nothing to map to, so the call is rejected before any API request or Langfuse trace is made. The family id (or 'unknown') is logged at error level first, so the log line identifies whose configuration is broken.

Source

Thrown at app/models/provider/openai.rb:111

  # Budget available for a one-shot (non-chat) request's full input,
  # excluding reserved response tokens AND the system/instructions prompt.
  # Drives the batch slicer for the auto_categorize / auto_detect_merchants /
  # enhance_provider_merchants calls — each ships ~200–400 tokens of
  # instructions + JSON schema that aren't counted in `fixed_tokens`.
  def max_input_tokens
    [ context_window - max_response_tokens - system_prompt_reserve, 256 ].max
  end

  def max_items_per_call
    positive_budget(ENV["LLM_MAX_ITEMS_PER_CALL"], Setting.llm_max_items_per_call, 25)
  end

  def auto_categorize(transactions: [], user_categories: [], model: "", family: nil, json_mode: nil)
    with_provider_response do
      if user_categories.blank?
        family_id = family&.id || "unknown"
        Rails.logger.error("Cannot auto-categorize transactions for family #{family_id}: no categories available")
        raise Error, "No categories available for auto-categorization"
      end

      effective_model = model.presence || @default_model

      trace = create_langfuse_trace(
        name: "openai.auto_categorize",
        input: { transactions: transactions, user_categories: user_categories }
      )

      batches = slice_for_context(transactions, fixed: user_categories)

      result = batches.flat_map do |batch|
        AutoCategorizer.new(
          client,
          model: effective_model,
          transactions: batch,
          user_categories: user_categories,
          custom_provider: custom_provider?,

View on GitHub (pinned to e69894adb9)

Solutions

  1. Ensure the family has at least one category before offering/running auto-categorization (seed default categories at family creation).
  2. Guard at the call site: skip the job unless family.categories.active.exists?.
  3. If categories were deleted mid-flight, re-trigger after the user restores or recreates categories.
  4. In tests, pass user_categories: [ { name: "Groceries" } ] (or create fixture categories) so the blank check passes.

Example fix

# before
AutoCategorizeJob.perform_later(family.id)

# after
return if family.categories.active.empty?
AutoCategorizeJob.perform_later(family.id)
Defensive patterns

Strategy: validation

Validate before calling

return if family.categories.active.none? # or seed defaults at family creation
SeedCategories.call(family) if family.categories.active.none?

Try / catch

begin
  provider.auto_categorize(transactions: txns, user_categories: cats, family: family)
rescue Provider::Openai::Error => e
  return { skipped: true } if e.message.include?("No categories available")
  raise
end

Prevention

When it happens

Trigger: Invoking auto-categorization for a brand-new family that never created categories; a family whose categories were all deleted or archived leaving none active; calling the method programmatically without forwarding the categories argument (family: nil plus empty defaults).

Common situations: Onboarding flows that trigger categorization before the default category set is provisioned;RSpec/VCR tests stubbing family but not categories; background jobs replaying old payloads after a user wiped their categories; a caller passing categories under a different keyword and silently hitting the [] default.

Related errors


AI-assisted analysis of we-promise/sure@e69894adb9 (2026-08-21). Data as JSON: /api/errors/cc78447fd0197348. Report an issue: GitHub.