# Buggy code
def callback(_, pdu):
    with PreserveLoggingContext(ctx):
        if not check_event_content_hash(pdu):
            # let's try to distinguish between failures because the event was
            # redacted (which are somewhat expected) vs actual ball-tampering
            # incidents.
            #
            # This is just a heuristic, so we just assume that if the keys are
            # about the same between the redacted and received events, then the
            # received event was probably a redacted copy (but we then use our
            # *actual* redacted copy to be on the safe side.)
            redacted_event = prune_event(pdu)
            if set(redacted_event.keys()) == set(pdu.keys()) and set(
                six.iterkeys(redacted_event.content)
            ) == set(six.iterkeys(pdu.content)):
                logger.info(
                    "Event %s seems to have been redacted; using our redacted copy",
                    pdu.event_id,
                )
            else:
                logger.warning(
                    "Event %s content has been tampered, redacting",
                    pdu.event_id,
                )
            return redacted_event

        if self.spam_checker.check_event_for_spam(pdu):
            logger.warning(
                "Event contains spam, redacting %s: %s",
                pdu.event_id,
                pdu.get_pdu_json(),
            )
            return prune_event(pdu)

        return pdu


# Traceback (AttributeError)
2020-02-24 14:00:10,088 - synapse.federation.federation_client - 421 - WARNING - POST-1957- Failed to send_join via matrix.org
...
Traceback (most recent call last):
File "/home/synapse/matrixtest/synapse/federation/federation_client.py", line 402, in _try_destination_list
res = await callback(destination)
File "/home/synapse/matrixtest/synapse/federation/federation_client.py", line 589, in send_request
valid_pdus_map = {p.event_id: p for p in valid_pdus}
File "/home/synapse/matrixtest/synapse/federation/federation_client.py", line 589, in <dictcomp>
valid_pdus_map = {p.event_id: p for p in valid_pdus}
AttributeError: 'coroutine' object has no attribute 'event_id'
2020-02-24 14:00:10,154 - synapse.http.server - 81 - INFO - POST-1957- <SynapseRequest at 0x7ff1bf3efd30 method='POST' uri='/_matrix/client/r0/join/%23synapse%3Amatrix.org' clientproto='HTTP/1.1' site=8447> SynapseError: 502 - Failed to send_join via any server

# Fixed code
def callback(_, pdu: EventBase):
    with PreserveLoggingContext(ctx):
        if not check_event_content_hash(pdu):
            # let's try to distinguish between failures because the event was
            # redacted (which are somewhat expected) vs actual ball-tampering
            # incidents.
            #
            # This is just a heuristic, so we just assume that if the keys are
            # about the same between the redacted and received events, then the
            # received event was probably a redacted copy (but we then use our
            # *actual* redacted copy to be on the safe side.)
            redacted_event = prune_event(pdu)
            if set(redacted_event.keys()) == set(pdu.keys()) and set(
                six.iterkeys(redacted_event.content)
            ) == set(six.iterkeys(pdu.content)):
                logger.info(
                    "Event %s seems to have been redacted; using our redacted copy",
                    pdu.event_id,
                )
            else:
                logger.warning(
                    "Event %s content has been tampered, redacting",
                    pdu.event_id,
                )
            return redacted_event

        if self.spam_checker.check_event_for_spam(pdu):
            logger.warning(
                "Event contains spam, redacting %s: %s",
                pdu.event_id,
                pdu.get_pdu_json(),
            )
            return prune_event(pdu)

        return pdu