{"record":{"id":"b31303c44a7d87c5","repo":"opf/openproject","slug":"failed-to-parse-jira-api-response-message","errorCode":null,"errorMessage":"Failed to parse Jira API response: %{message}","messagePattern":"Failed to parse Jira API response: %(.+?)","errorType":"exception","errorClass":"Import::JiraClient::ParseError","httpStatus":null,"severity":"error","filePath":"app/services/import/jira_client.rb","lineNumber":320,"sourceCode":"    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":302,"sourceCodeEnd":324,"githubUrl":"https://github.com/opf/openproject/blob/d9742c43f3424c34b63550f8c03f201fe5c3040c/app/services/import/jira_client.rb#L302-L324","documentation":"handle_response parses every 2xx body with JSON.parse; a JSON::ParserError is re-raised as Import::JiraClient::ParseError with this message. It means the HTTP request itself succeeded but the body is not JSON — almost always an HTML page (SSO login form, proxy error page) or an empty body arriving with a 200 status.","triggerScenarios":"Any Import::JiraClient API call where @url points at a non-Jira page (e.g. https://jira.example.com/login so /rest/api/2/... returns the login HTML), an SSO/SAML layer in front of Jira redirects REST calls to an HTML login page, an intermediary (proxy, captive portal, WAF) returns an HTML block page with 200, or the URL scheme/host is wrong so the response comes from an unrelated server.","commonSituations":"Saving the Jira login page URL instead of the base URL in the import settings; SAML/OIDC web SSO in front of Jira that does not exempt /rest/api; reverse proxies serving custom error pages; typo'd domains resolving to a parked page; API token invalid so an HTML error page is returned instead of JSON.","solutions":["Verify the base URL directly: curl -H 'Authorization: Bearer <token>' https://jira.example.com/rest/api/2/serverInfo must return JSON","If SSO fronts Jira, configure it to bypass authentication for /rest/api/* (e.g. basic auth or anonymous REST access) so API calls are not redirected to an HTML login","Inspect %{message}: JSON::ParserError includes the offending text/offset — '<html' fragments confirm an interceptor page","Correct the saved URL to the bare Jira base URL and re-test the connection from the import settings"],"exampleFix":"# before — import settings URL points at an HTML page\n#   Jira URL: https://jira.example.com/login\n#   → GET /rest/api/2/serverInfo returns login HTML → ParseError\n\n# after\n#   Jira URL: https://jira.example.com\n#   curl -H \"Authorization: Bearer $TOKEN\" \\\n#     https://jira.example.com/rest/api/2/serverInfo   # returns JSON","handlingStrategy":"try-catch","validationCode":"# smoke-test that the configured base URL speaks JSON before importing\nresp = OpenProject::SsrfProtection.get(\"#{jira_url}/rest/api/2/serverInfo\",\n                                       headers: { \"Authorization\" => \"Bearer #{token}\" })\nabort \"base URL does not serve the Jira REST API\" unless resp.is_a?(Net::HTTPSuccess) && resp[\"Content-Type\"].to_s.include?(\"application/json\")","typeGuard":null,"tryCatchPattern":"begin\n  data = client.issues(jql: \"project = X\")\nrescue Import::JiraClient::ParseError => e\n  # 2xx + non-JSON body: almost always an SSO login/proxy page in front of Jira\n  Rails.logger.error(\"Jira returned non-JSON (SSO/proxy interceptor?): #{e.message}\")\n  raise\nend","preventionTips":["Always configure the bare Jira base URL; never a login or portal path","Exempt /rest/api/* from web SSO in front of Jira (basic/API-token auth path)","Add a serverInfo JSON smoke test to import runbooks to catch interceptor pages early"],"tags":["jira","json","parsing","import","proxy","sso"],"backgroundTag":"invalid-json-response","analyzedSha":"d9742c43f3424c34b63550f8c03f201fe5c3040c","analyzedAt":"2026-08-21T14:40:06.829Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}