{"record":{"id":"e6e7b9073bad39b0","repo":"opf/openproject","slug":"jira-api-returned-error-status-status","errorCode":null,"errorMessage":"Jira API returned error status %{status}","messagePattern":"Jira API returned error status %(.+?)","errorType":"exception","errorClass":"Import::JiraClient::ApiError","httpStatus":null,"severity":"error","filePath":"app/services/import/jira_client.rb","lineNumber":309,"sourceCode":"        params:,\n        http_options: HTTP_OPTIONS\n      )\n    rescue SsrfFilter::PrivateIPAddress\n      raise SsrfError, I18n.t(\"admin.jira.client.ssrf_blocked\")\n    rescue SsrfFilter::Error, SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH => e\n      raise ConnectionError, I18n.t(\"admin.jira.client.connection_error\", message: e.message)\n    rescue OpenSSL::SSL::SSLError => e\n      raise ConnectionError, I18n.t(\"admin.jira.client.ssl_error\", message: e.message)\n    rescue Timeout::Error => e\n      raise ConnectionError, I18n.t(\"admin.jira.client.connection_timeout\", message: e.message)\n    end\n\n    def handle_response(response)\n      status = response.code.to_i\n      if response.is_a?(Net::HTTPSuccess)\n        parse_json(response)\n      else\n        raise ApiError.new(\n          I18n.t(\"admin.jira.client.#{status}_error\", status:, default: :\"admin.jira.client.api_error\"),\n          status:,\n          response_body: response.body.to_s\n        )\n      end\n    end\n\n    def parse_json(response)\n      JSON.parse(response.body)\n    rescue JSON::ParserError => e\n      raise ParseError, I18n.t(\"admin.jira.client.parse_error\", message: e.message)\n    end\n  end\nend\n","sourceCodeStart":291,"sourceCodeEnd":324,"githubUrl":"https://github.com/opf/openproject/blob/d9742c43f3424c34b63550f8c03f201fe5c3040c/app/services/import/jira_client.rb#L291-L324","documentation":"This is Import::JiraClient#handle_response's generic branch: a non-2xx status that has no dedicated translation key falls back to admin.jira.client.api_error ('Jira API returned error status %{status}'). The raised ApiError still carries status and response_body, so the real cause must be read from those fields. Typical statuses behind it: 403 (missing permission), 404 (wrong base URL/context path), 500/502/503 (Jira-side failure).","triggerScenarios":"Any JiraClient call returning e.g. 404 because the configured URL misses the context path (jira.example.com instead of jira.example.com/jira), 403 because the token lacks the needed project permission, or 5xx during Jira maintenance — none of which have status-specific locale keys.","commonSituations":"Wrong or half-configured Jira base URL (404 on every endpoint); token owner can authenticate but cannot browse the projects being imported (403); Jira restarted/out of memory mid-import (503); reverse proxy in front of Jira returning its own error codes.","solutions":["Inspect the exception's status and response_body (or the logged job output) — Jira's JSON error messages identify the exact problem.","For 404: fix the base URL in the Jira settings (include the context path if Jira is not at root) and re-test the connection.","For 403: grant the token owner browse/admin permission on the projects being imported.","For 5xx: check Jira health/logs, then retry the import run once Jira is stable."],"exampleFix":"# before\nrescue Import::JiraClient::ApiError => e\n  Rails.logger.error e.message\n\n# after\nrescue Import::JiraClient::ApiError => e\n  Rails.logger.error \"Jira #{e.status}: #{e.response_body}\"","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  client.get(path)\nrescue Import::JiraClient::ApiError => e\n  Rails.logger.error \"Jira #{e.status}: #{e.response_body}\"\n  raise if [500, 502, 503].include?(e.status) # retryable upstream failures\nend","preventionTips":["Always log e.status and e.response_body, never just the interpolated message — the body carries Jira's real reason.","Validate the base URL with the test-connection action (which hits a known endpoint) before long runs.","Classify statuses early: 401/403 are configuration problems, 5xx are transient."],"tags":["jira-import","http-error","api-client","integration"],"backgroundTag":"http-error-status","analyzedSha":"d9742c43f3424c34b63550f8c03f201fe5c3040c","analyzedAt":"2026-08-21T14:40:06.829Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}