Skip to content

e.errors does not contain the most recent error #223

Open
@strawgate

Description

@strawgate

As described here: https://elasticsearch-py.readthedocs.io/en/latest/exceptions.html

When a TlsError (or other TransportError) is initially raised, it contains a message attribute, "Cannot connect to host..." and an errors attribute which is a tuple of the causing errors, for my TlsError example, e.errors is a tuple with 1 item which is a ClientConnectorCertificateError.

When the transport invokes a request with retries (default: 3), where all retries fail (in my example due to an untrusted certificate), it takes each error encountered by each retry and appends them to a list, except for the error from the final retry which is the exception it ends up raising. Before raising that final exception, it takes the list of previous exceptions and overwrites e.errors with the list of exceptions raised during previous attempts.

This creates some unfortunate behavior where e.errors contains a list of errors which include what caused each error, but the actual most recent error (from try number 4) has now lost its e.error, it having been replaced with the errors from the 3 retries:

TLSError{ <--- Retry 3
  message: "Cannot connect to host..."
  errors: (
    TLSError{ <--- Retry 2
      message: "Cannot connect to host..."
      errors: (
        ClientConnectorCertificateError,
      )
    },
    TLSError{ <--- Retry 1
      message: "Cannot connect to host..."
      errors: (
        ClientConnectorCertificateError,
      )
    },
    TLSError{ <--- Initial Request
      message: "Cannot connect to host..."
      errors: (
        ClientConnectorCertificateError,
      )
    }
  )
}

Where this becomes especially problematic is when max_retries is set to zero and this logic results in e.errors being replaced with an empty tuple.

TLSError{ <-- Initial Request
  message: "Cannot connect to host..."
  errors: ( )
}

And having lost access to the underlying cause of the TLSError. It seems the only way to get access to the underlying error is to always have at least 1 retry so you've got at least 1 entry in the tuple to inspect? Though the entry will still be from the first request and not the last one so hopefully the errors are the same.

It seems perhaps that if retries is set to 0 then e.errors should not be overwritten, returning:

TLSError{
  message: Cannot connect to host..."
  errors: (
    ClientConnectorCertificateError,
  )
}

Or perhaps e should always be appended to e.errors before raising it?

TLSError{ <--- Retry 3
  message: "Cannot connect to host..."
  errors: (
    TLSError{ <--- Retry 3
      message: "Cannot connect to host..."
      errors: (
        ClientConnectorCertificateError,
      )
    },
    TLSError{ <--- Retry 2
      message: "Cannot connect to host..."
      errors: (
        ClientConnectorCertificateError,
      )
    },
    TLSError{ <--- Retry 1
      message: "Cannot connect to host..."
      errors: (
        ClientConnectorCertificateError,
      )
    },
    TLSError{ <--- Initial Request
      message: "Cannot connect to host..."
      errors: (
        ClientConnectorCertificateError,
      )
    }
  )
}

or in the 0 retry case:

TLSError{ <--- Initial Request
  message: "Cannot connect to host..."
  errors: (
    TLSError{ <--- Initial Request
      message: "Cannot connect to host..."
      errors: (
        ClientConnectorCertificateError,
      )
    }
  )
}

Metadata

Metadata

Assignees

Labels

Area: ClientManually written code that fits in no other areaCategory: BugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions