opf/openproject · error · Import::JiraClient::ConnectionError

Connection to Jira server timed out: %{message}

Error message

Connection to Jira server timed out: %{message}

What it means

In download_attachment, Timeout::Error (including Net::OpenTimeout/Net::ReadTimeout subclasses) is wrapped into Import::JiraClient::ConnectionError with this message. The client uses the frozen HTTP_OPTIONS = { open_timeout: 30, read_timeout: 30 }, so exceeding either limit while connecting to or streaming a Jira attachment raises; the original timeout detail is in %{message}.

Source

Thrown at app/services/import/jira_client.rb:275

          tempfile = Tempfile.create(filename, binmode: true)
          response.read_body do |chunk|
            tempfile.write chunk
          end
          yield tempfile
        else
          status = response.code.to_i
          raise ApiError.new(I18n.t("admin.jira.client.api_error", status:), status:, response_body: response.body)
        end
      end
      nil
    rescue SsrfFilter::PrivateIPAddress
      raise SsrfError, I18n.t("admin.jira.client.ssrf_blocked")
    rescue SsrfFilter::Error => e
      raise ConnectionError, I18n.t("admin.jira.client.connection_error", message: e.message)
    rescue OpenSSL::SSL::SSLError => e
      raise ConnectionError, I18n.t("admin.jira.client.ssl_error", message: e.message)
    rescue Timeout::Error => e
      raise ConnectionError, I18n.t("admin.jira.client.connection_timeout", message: e.message)
    ensure
      File.unlink(tempfile) if tempfile
    end

    private

    def get(path, params: {})
      response = get_response(path, params:)
      handle_response(response)
    end

    def get_response(path, params: {})
      OpenProject::SsrfProtection.get(
        "#{@url}#{path}",
        headers: @headers,
        params:,
        http_options: HTTP_OPTIONS
      )

View on GitHub (pinned to d9742c43f3)

Solutions

  1. Measure actual connectivity from the OpenProject host: curl -o /dev/null -w 'connect=%{time_connect} total=%{time_total}' '<attachment-url>'
  2. If transfers legitimately exceed 30s, raise the timeouts (HTTP_OPTIONS is a frozen constant — patch it via an initializer/upstream change) or reduce attachment sizes
  3. Retry the operation: transient congestion is the most common cause and a second attempt often succeeds
  4. For recurring stalls, check path MTU/VPN encapsulation and Jira-side GC pauses

Example fix

# before
client.download_attachment(content_url, filename) { |tf| attach(tf) }

# after — retry transient timeouts with backoff
attempts = 0
begin
  attempts += 1
  client.download_attachment(content_url, filename) { |tf| attach(tf) }
rescue Import::JiraClient::ConnectionError => e
  raise if attempts >= 3 || !e.message.include?("timed out")
  sleep(2**attempts)
  retry
end
Defensive patterns

Strategy: retry

Try / catch

attempts = 0
begin
  attempts += 1
  client.download_attachment(content_url, filename) { |tf| attach(tf) }
rescue Import::JiraClient::ConnectionError => e
  raise if attempts >= 3 || !e.message.include?("timed out")
  sleep(2**attempts)
  retry
end

Prevention

When it happens

Trigger: client.download_attachment where the TCP/TLS connect takes over 30s (typically a firewall silently dropping packets so the connect hangs), or the streamed attachment body stalls/pushes data slower than the 30s read timeout allows (large attachments on a slow or throttled link, paused Jira node).

Common situations: Containerized OpenProject with no route to the Jira network (packets blackholed instead of rejected); VPN/MTU problems that stall mid-transfer of large attachments; bulk imports hitting an overloaded Jira so responses trickle; nightly backup windows slowing the Jira host.

Understand the failure class

Related errors


AI-assisted analysis of opf/openproject@d9742c43f3 (2026-08-21). Data as JSON: /api/errors/9c5a94edafd61a61. Report an issue: GitHub.