{"record":{"id":"848e56841353084f","repo":"opf/openproject","slug":"jira-api-returned-a-401-error-your-authentication","errorCode":null,"errorMessage":"Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator.","messagePattern":"Jira API returned a 401 error\\. Your authentication token may have expired or lack the required permissions\\. Please ensure the token belongs to a Jira administrator\\.","errorType":"exception","errorClass":"Import::JiraClient::ApiError","httpStatus":401,"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":"Import::JiraClient#handle_response turns every non-2xx HTTP response into an ApiError whose message comes from the locale key admin.jira.client.401_error. A 401 from Jira means the Bearer token was rejected: expired, revoked/regenerated, wrong value, or the token owner is not a Jira administrator as required by the importer.","triggerScenarios":"Any JiraClient request (mypermissions, project/issue fetching during a run, or the admin 'test connection') after the API token was revoked or regenerated in Jira, or after the owning Jira user lost admin rights.","commonSituations":"Atlassian API tokens expire or get rotated; someone regenerated the token in Jira while an import run was queued; token pasted with surrounding whitespace; the settings were saved with an empty token field so a stale/blank header is sent.","solutions":["Create a fresh API token in Jira for an administrator account, update it in Admin → Import → Jira settings, save and run the test connection.","Confirm the token owner actually holds Jira administrator permission (the importer calls /rest/api/2/mypermissions which requires it).","If the error appears mid-run after working before, the token was likely revoked/rotated — re-enter it and restart the import run."],"exampleFix":"# before\nbegin\n  client.mypermissions\nrescue Import::JiraClient::ApiError => e\n  puts e.message\nend\n\n# after\nbegin\n  client.mypermissions\nrescue Import::JiraClient::ApiError => e\n  if e.status == 401\n    raise 'Re-enter the Jira API token (expired or revoked) in admin settings'\n  end\n  raise\nend","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  client.mypermissions\nrescue Import::JiraClient::ApiError => e\n  raise CredentialsExpired, 're-enter Jira API token' if e.status == 401\n  raise\nend","preventionTips":["Treat 401 as a terminal condition for the run: stop, surface a re-credential form, do not retry with the same token.","Run the connection test before each import run to catch rotated/expired tokens up front.","Use a dedicated, administrator-owned Jira token so unrelated permission changes don't break it."],"tags":["jira-import","http-401","api-token","authentication"],"backgroundTag":"http-401-unauthorized","analyzedSha":"d9742c43f3424c34b63550f8c03f201fe5c3040c","analyzedAt":"2026-08-21T14:40:06.829Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}