{"record":{"id":"b656b562c5e267d2","repo":"danbooru/danbooru","slug":"autotagger-failed-code-response-code","errorCode":null,"errorMessage":"Autotagger failed (code #{response.code})","messagePattern":"Autotagger failed \\(code #(.+?)\\)","errorType":"exception","errorClass":"AutotaggerClient::Error","httpStatus":null,"severity":"error","filePath":"app/logical/autotagger_client.rb","lineNumber":44,"sourceCode":"\n    response = http.post(\"#{autotagger_url}/evaluate\", form: { file: HTTP::FormData::File.new(file), threshold: confidence, format: \"json\" })\n    return {} if !response.status.success?\n\n    response.parse.first[\"tags\"].with_indifferent_access\n  end\n\n  # Get the AI tags for an image as an array of AITags. Creates new tags if they don't already exist. Raises an error\n  # if the API call fails.\n  #\n  # @param file [File] The image file.\n  # @param limit [Integer] The maximum number of tags to return.\n  # @param confidence [Float] The minimum confidence level for each tag.\n  # @return [Array<AITag>] A list of new AI tags for this image. The `media_asset` field will be nil.\n  def evaluate!(file, limit: 50, confidence: 0.01)\n    return [] if autotagger_url.blank?\n\n    response = http.post(\"#{autotagger_url}/evaluate\", form: { file: HTTP::FormData::File.new(file), threshold: confidence, format: \"json\" })\n    raise Error, \"Autotagger failed (code #{response.code})\" if !response.status.success?\n\n    tag_names_with_scores = response.parse.first[\"tags\"]\n    tag_names = tag_names_with_scores.keys\n    tags = Tag.where(name: tag_names).to_a\n\n    missing_tags = tag_names - tags.pluck(:name)\n    missing_tags.each do |name|\n      tags << Tag.find_or_create_by_name(name, skip_name_validation: true)\n    end\n\n    tags.map do |tag|\n      score = (100 * tag_names_with_scores[tag.name]).round\n      AITag.new(tag: tag, score: score)\n    end.sort_by(&:score)\n  end\nend\n","sourceCodeStart":26,"sourceCodeEnd":61,"githubUrl":"https://github.com/danbooru/danbooru/blob/91fec095644078f87729db85d8f7486de5daf83d/app/logical/autotagger_client.rb#L26-L61","documentation":"AutotaggerClient#evaluate! POSTs the image file to <autotagger_url>/evaluate and raises AutotaggerClient::Error carrying the HTTP status code whenever the response is not 2xx. It is the strict variant used by the AI-tagging features (the tagme button and ai_tags controller actions); the soft evaluate returns {} on failure instead. A blank autotagger_url short-circuits to [] (feature disabled), so this error always means a configured autotagger service answered with an error status.","triggerScenarios":"Triggering AI tagging (POST /uploads/:id/autotag or the ai_tags actions) while the autotagger service returns 4xx/5xx: autotagger_url pointing at the wrong host or path (404), the model worker down or not yet loaded (502/503 behind a proxy), an unreadable or oversized image, or a client/service version mismatch.","commonSituations":"Self-hosted autotagger container down or still loading its model on first request; Danbooru.config.autotagger_url misconfigured (wrong port, wrong base path — note the client appends /evaluate itself); reverse-proxy timeouts surfacing as 502/504; transient failures during bulk AI-tagging jobs.","solutions":["Reproduce directly: curl -F file=@image.jpg -F threshold=0.01 -F format=json <autotagger_url>/evaluate and read the service's real error","Check Danbooru.config.autotagger_url — correct scheme/host/port, and no /evaluate already appended","Inspect the autotagger service logs and restart it; watch for model-load delay on the first request after startup","On non-critical paths, switch to the soft AutotaggerClient#evaluate, which returns {} instead of raising"],"exampleFix":"# before (raises AutotaggerClient::Error on any non-2xx)\ntags = AutotaggerClient.new.evaluate!(media_asset.file.open)\n\n# after (degrade gracefully when AI tagging is optional)\ntags = begin\n  AutotaggerClient.new.evaluate!(media_asset.file.open)\nrescue AutotaggerClient::Error => e\n  Rails.logger.warn(\"Autotagger unavailable: #{e.message}\")\n  []\nend","handlingStrategy":"fallback","validationCode":"# Skip AI tagging when the service isn't configured (blank URL already returns [] server-side)\nreturn if AutotaggerClient.new.autotagger_url.blank?","typeGuard":null,"tryCatchPattern":"begin\n  ai_tags = client.evaluate!(file)\nrescue AutotaggerClient::Error => e\n  Rails.logger.warn(\"autotagger: #{e.message}\")\n  ai_tags = [] # degrade gracefully; AI tags are optional metadata\nend","preventionTips":["Use the soft evaluate (returns {} on failure) wherever AI tagging is best-effort; reserve evaluate! for flows that must fail loudly","Health-check the autotagger /evaluate endpoint and alert before users hit the tagme button","Smoke-test autotagger_url after each deploy; the client appends /evaluate itself, so don't double it in config"],"tags":["ruby","http","autotagger","ai-tags","upload"],"backgroundTag":"upstream-service-error","analyzedSha":"91fec095644078f87729db85d8f7486de5daf83d","analyzedAt":"2026-08-24T06:18:22.875Z","schemaVersion":2},"datasetVersion":"2026-08-24T07:17:09.176Z"}