What does "TransactionError: This transaction has expired" mean?

I just found this in an app’s session’s log:
image

The crash happens when the app calls a server function defined in an uplink and tries to log its failure:

# the server call generated an exception, which should have been logged
try:
    result = anvil.server.call(function_name)
except Exception as e:
    Logger.log(f'start_next_uplink_machine: Crash: "{e}"', 'queue manager')

The logging is managed by the Logger dependency app, and it crashes when it tries to add a row to the log table:

# the log function is defined in the Logger dependency
@anvil.server.callable
@anvil.server.http_endpoint('/log')
def log(what=None, who=None, **kwargs):
    # [...] other stuff going on here
    app_tables.log.add_row(when=datetime.datetime.now(), who=who, what=what)

I would understand add_row generating exceptions mentioning transaction failures, conflicts or similar problems, but I don’t understand how a transaction could expire.

In general database terms, I can see how a transaction might expire, if it is not being used to read or write for awhile, and is preventing other work from getting done, e.g., via database locks. It would not surprise me if a lock had a timeout.

I thought about something like that could be happening, but while the try - catch wraps a call to an uplink function, which could be playing with transactions, could be slow or victim of all kind of connection issues, this error happens later, after something went wrong in that uplink function call, while managing the exception.

It crashes on the first line of a function, while trying to add a row to a table. At this time none of the functions in the stack has the in_transaction decorator or explicitly manages any transaction, so I don’t understand what could be expiring.