{"record":{"id":"948b51f9bab814b4","repo":"we-promise/sure","slug":"failed-to-auto-categorize-transactions-result-e","errorCode":null,"errorMessage":"Failed to auto-categorize transactions: #{result.error.message}","messagePattern":"Failed to auto-categorize transactions: #(.+?)","errorType":"exception","errorClass":"Family::AutoCategorizer::Error","httpStatus":null,"severity":"error","filePath":"app/models/family/auto_categorizer.rb","lineNumber":33,"sourceCode":"    else\n      Rails.logger.info(\"Auto-categorizing #{scope.count} transactions for family #{family.id}\")\n    end\n\n    categories_input = user_categories_input\n\n    if categories_input.empty?\n      Rails.logger.error(\"Cannot auto-categorize transactions for family #{family.id}: no categories available\")\n      return 0\n    end\n\n    result = llm_provider.auto_categorize(\n      transactions: transactions_input,\n      user_categories: categories_input,\n      family: family\n    )\n\n    unless result.success?\n      raise Error, \"Failed to auto-categorize transactions: #{result.error.message}\"\n    end\n\n    modified_count = 0\n    scope.each do |transaction|\n      auto_categorization = result.data.find { |c| c.transaction_id == transaction.id }\n\n      category_id = categories_input.find { |c| c[:name] == auto_categorization&.category_name }&.dig(:id)\n\n      if category_id.present?\n        was_modified = transaction.enrich_attribute(\n          :category_id,\n          category_id,\n          source: \"ai\"\n        )\n        transaction.lock_attr!(:category_id)\n        # enrich_attribute returns true if the transaction was actually modified\n        modified_count += 1 if was_modified\n      end","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/we-promise/sure/blob/e69894adb92547273377398c15f45c979cd9416a/app/models/family/auto_categorizer.rb#L15-L51","documentation":"Raised when llm_provider.auto_categorize(transactions:, user_categories:, family:) returns an unsuccessful Result; the message embeds result.error.message from the provider client. Causes live in the LLM provider call itself: auth failures, rate limits, timeouts, or responses that fail the provider's parsing into Result(success?: false).","triggerScenarios":"The provider HTTP request fails or returns an error payload while categorizing a batch — expired/revoked API key, 429 rate limit, network timeout, or a malformed model response that cannot be parsed.","commonSituations":"Large batches hitting requests-per-minute or token limits; rotated API keys; provider incidents; oversized transaction payloads exceeding model context.","solutions":["Read the embedded result.error.message — it is the provider's own error text and distinguishes auth vs rate limit vs parsing","Retry after the rate-limit window or reduce the transaction_ids batch size","Verify provider credentials (ENV) are still valid; check the provider status page","Rescue Family::AutoCategorizer::Error at the job layer and mark the job failed with the message instead of crashing the queue"],"exampleFix":"// before\nFamily::AutoCategorizer.new(family, transaction_ids: ids).auto_categorize\n\n// after\nbegin\n  Family::AutoCategorizer.new(family, transaction_ids: ids).auto_categorize\nrescue Family::AutoCategorizer::Error => e\n  Rails.logger.error(\"Auto-categorize failed: #{e.message}\")\n  raise # let the job retry with backoff\nend","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"rescue Family::AutoCategorizer::Error => e and read the embedded provider message: retry with backoff for rate-limit/network causes, fail fast for auth causes; never swallow silently since the batch was already charged/queued","preventionTips":["Batch transaction_ids in bounded sizes to stay under provider rate limits","Alert on auth-type provider errors — they mean credential drift","Surface result.error.message verbatim in job failure metadata for triage"],"tags":["llm","auto-categorization","provider-error","rate-limit"],"backgroundTag":"llm-api-error","analyzedSha":"e69894adb92547273377398c15f45c979cd9416a","analyzedAt":"2026-08-21T18:22:41.165Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}