{"record":{"id":"cc78447fd0197348","repo":"we-promise/sure","slug":"no-categories-available-for-auto-categorization-cc7844","errorCode":null,"errorMessage":"No categories available for auto-categorization","messagePattern":"No categories available for auto-categorization","errorType":"exception","errorClass":"Provider::Openai::Error","httpStatus":null,"severity":"error","filePath":"app/models/provider/openai.rb","lineNumber":111,"sourceCode":"  # Budget available for a one-shot (non-chat) request's full input,\n  # excluding reserved response tokens AND the system/instructions prompt.\n  # Drives the batch slicer for the auto_categorize / auto_detect_merchants /\n  # enhance_provider_merchants calls — each ships ~200–400 tokens of\n  # instructions + JSON schema that aren't counted in `fixed_tokens`.\n  def max_input_tokens\n    [ context_window - max_response_tokens - system_prompt_reserve, 256 ].max\n  end\n\n  def max_items_per_call\n    positive_budget(ENV[\"LLM_MAX_ITEMS_PER_CALL\"], Setting.llm_max_items_per_call, 25)\n  end\n\n  def auto_categorize(transactions: [], user_categories: [], model: \"\", family: nil, json_mode: nil)\n    with_provider_response do\n      if user_categories.blank?\n        family_id = family&.id || \"unknown\"\n        Rails.logger.error(\"Cannot auto-categorize transactions for family #{family_id}: no categories available\")\n        raise Error, \"No categories available for auto-categorization\"\n      end\n\n      effective_model = model.presence || @default_model\n\n      trace = create_langfuse_trace(\n        name: \"openai.auto_categorize\",\n        input: { transactions: transactions, user_categories: user_categories }\n      )\n\n      batches = slice_for_context(transactions, fixed: user_categories)\n\n      result = batches.flat_map do |batch|\n        AutoCategorizer.new(\n          client,\n          model: effective_model,\n          transactions: batch,\n          user_categories: user_categories,\n          custom_provider: custom_provider?,","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/provider/openai.rb#L93-L129","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Ensure the family has at least one category before offering/running auto-categorization (seed default categories at family creation).","Guard at the call site: skip the job unless family.categories.active.exists?.","If categories were deleted mid-flight, re-trigger after the user restores or recreates categories.","In tests, pass user_categories: [ { name: \"Groceries\" } ] (or create fixture categories) so the blank check passes."],"exampleFix":"# before\nAutoCategorizeJob.perform_later(family.id)\n\n# after\nreturn if family.categories.active.empty?\nAutoCategorizeJob.perform_later(family.id)","handlingStrategy":"validation","validationCode":"return if family.categories.active.none? # or seed defaults at family creation\nSeedCategories.call(family) if family.categories.active.none?","typeGuard":null,"tryCatchPattern":"begin\n  provider.auto_categorize(transactions: txns, user_categories: cats, family: family)\nrescue Provider::Openai::Error => e\n  return { skipped: true } if e.message.include?(\"No categories available\")\n  raise\nend","preventionTips":["Provision default categories at family creation so the precondition always holds.","Guard jobs with family.categories.active.exists? before enqueuing categorization work.","Pass user_categories explicitly at every call site — never rely on the empty default."],"tags":["openai","auto-categorization","llm","precondition","empty-input"],"backgroundTag":"invalid-request-state","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}