b4 review tui crashes while openining serieses

Mark Brown <[email protected]> Tue, 16 Jun 2026 13:06:25 +0100
Newsgroups org.kernel.linux.tools
Message-ID <[email protected]>
Hi,

Recently (since the support for cancelling network requests came in?)
I've started getting crashes sometimes when opening serieses, I think
only when opening them for the first time, I've not been explicitly
cancelling anything.  Retrying generally works fine, it's some race or
old state:

╭──────────────────────────────────── Traceback (most recent call last) ─────────────────────────────────────╮
│ /home/broonie/git/b4/.venv/lib/python3.13/site-packages/textual/worker.py:370 in _run                      │
│                                                                                                            │
│   367 │   │   │   self.state = WorkerState.RUNNING                                                         │
│   368 │   │   │   app.log.worker(self)                                                                     │
│   369 │   │   │   try:                                                                                     │
│ ❱ 370 │   │   │   │   self._result = await self.run()                                                      │
│   371 │   │   │   except asyncio.CancelledError as error:                                                  │
│   372 │   │   │   │   self.state = WorkerState.CANCELLED                                                   │
│   373 │   │   │   │   self._error = error                                                                  │
│                                                                                                            │
│ ╭──────────────────────────────────────────────── locals ────────────────────────────────────────────────╮ │
│ │           app = TrackingApp(                                                                           │ │
│ │                 │   title='b4 review',                                                                 │ │
│ │                 │   classes={'-dark-mode'},                                                            │ │
│ │                 │   pseudo_classes={'dark', 'focus'}                                                   │ │
│ │                 )                                                                                      │ │
│ │         error = OperationCancelledError('Request cancelled')                                           │ │
│ │          self = <Worker                                                                                │ │
│ │                 │   ERROR                                                                              │ │
│ │                 │   name='_ws_work'                                                                    │ │
│ │                 │   description='<function                                                             │ │
│ │                 TrackingApp._checkout_new_series.<locals>._fetch_and_prepare at 0x7fb5'+9              │ │
│ │                 >                                                                                      │ │
│ │ worker_failed = WorkerFailed("Worker raised exception: OperationCancelledError('Request cancelled')")  │ │
│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                            │
│ /home/broonie/git/b4/.venv/lib/python3.13/site-packages/textual/worker.py:354 in run                       │
│                                                                                                            │
│   351 │   │   Returns:                                                                                     │
│   352 │   │   │   Return value of the work.                                                                │
│   353 │   │   """                                                                                          │
│ ❱ 354 │   │   return await (                                                                               │
│   355 │   │   │   self._run_threaded() if self._thread_worker else self._run_async()                       │
│   356 │   │   )                                                                                            │
│   357                                                                                                      │
│                                                                                                            │
│ ╭──────────────────────────────────────────────── locals ────────────────────────────────────────────────╮ │
│ │ self = <Worker                                                                                         │ │
│ │        │   ERROR                                                                                       │ │
│ │        │   name='_ws_work'                                                                             │ │
│ │        │   description='<function TrackingApp._checkout_new_series.<locals>._fetch_and_prepare at      │ │
│ │        0x7fb5'+9                                                                                       │ │
│ │        >                                                                                               │ │
│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                            │
│ /home/broonie/git/b4/.venv/lib/python3.13/site-packages/textual/worker.py:326 in _run_threaded             │
│                                                                                                            │
│   323 │   │                                                                                                │
│   324 │   │   loop = asyncio.get_running_loop()                                                            │
│   325 │   │   assert loop is not None                                                                      │
│ ❱ 326 │   │   return await loop.run_in_executor(None, runner, self._work)                                  │
│   327 │                                                                                                    │
│   328 │   async def _run_async(self) -> ResultType:                                                        │
│   329 │   │   """Run an async worker.                                                                      │
│                                                                                                            │
│ ╭──────────────────────────────────────────────── locals ────────────────────────────────────────────────╮ │
│ │ loop = <_UnixSelectorEventLoop running=True closed=False debug=False>                                  │ │
│ │ self = <Worker                                                                                         │ │
│ │        │   ERROR                                                                                       │ │
│ │        │   name='_ws_work'                                                                             │ │
│ │        │   description='<function TrackingApp._checkout_new_series.<locals>._fetch_and_prepare at      │ │
│ │        0x7fb5'+9                                                                                       │ │
│ │        >                                                                                               │ │
│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                            │
│ /usr/lib/python3.13/concurrent/futures/thread.py:59 in run                                                 │
│                                                                                                            │
│    56 │   │   │   return                                                                                   │
│    57 │   │                                                                                                │
│    58 │   │   try:                                                                                         │
│ ❱  59 │   │   │   result = self.fn(*self.args, **self.kwargs)                                              │
│    60 │   │   except BaseException as exc:                                                                 │
│    61 │   │   │   self.future.set_exception(exc)                                                           │
│    62 │   │   │   # Break a reference cycle with the exception 'exc'                                       │
│                                                                                                            │
│ ╭── locals ───╮                                                                                            │
│ │ self = None │                                                                                            │
│ ╰─────────────╯                                                                                            │
│                                                                                                            │
│ /home/broonie/git/b4/.venv/lib/python3.13/site-packages/textual/worker.py:309 in run_callable              │
│                                                                                                            │
│   306 │   │   def run_callable(work: Callable[[], ResultType]) -> ResultType:                              │
│   307 │   │   │   """Set the active worker, and call the callable."""                                      │
│   308 │   │   │   active_worker.set(self)                                                                  │
│ ❱ 309 │   │   │   return work()                                                                            │
│   310 │   │                                                                                                │
│   311 │   │   if (                                                                                         │
│   312 │   │   │   inspect.iscoroutinefunction(self._work)                                                  │
│                                                                                                            │
│ ╭──────────────────────────────────────────────── locals ────────────────────────────────────────────────╮ │
│ │ self = <Worker                                                                                         │ │
│ │        │   ERROR                                                                                       │ │
│ │        │   name='_ws_work'                                                                             │ │
│ │        │   description='<function TrackingApp._checkout_new_series.<locals>._fetch_and_prepare at      │ │
│ │        0x7fb5'+9                                                                                       │ │
│ │        >                                                                                               │ │
│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                            │
│ /home/broonie/git/b4/src/b4/review_tui/_tracking_app.py:1444 in _fetch_and_prepare                         │
│                                                                                                            │
│   1441 │   │                                                                                               │
│   1442 │   │   def _fetch_and_prepare() -> Tuple[b4.LoreSeries, bytes, str, str]:                          │
│   1443 │   │   │   with _quiet_worker():                                                                   │
│ ❱ 1444 │   │   │   │   msgs = b4.review.retrieve_series_messages(series, self._identifier)                 │
│   1445 │   │   │   │   self._refresh_msg_count(series, len(msgs))                                          │
│   1446 │   │   │   │   wantver = series.get('revision')                                                    │
│   1447 │   │   │   │   try:                                                                                │
│                                                                                                            │
│ ╭──────────────────────────────────────────────── locals ────────────────────────────────────────────────╮ │
│ │   self = TrackingApp(title='b4 review', classes={'-dark-mode'}, pseudo_classes={'dark', 'focus'})      │ │
│ │ series = {                                                                                             │ │
│ │          │   'track_id': 718,                                                                          │ │
│ │          │   'change_id':                                                                              │ │
│ │          '20260615-asoc-dt-bindings-mtk-btcvsd-snd-convert-to-dt-schema-611f77a4d16d',                 │ │
│ │          │   'revision': 4,                                                                            │ │
│ │          │   'subject': 'ASoC: dt-bindings: mtk-btcvsd-snd: Convert to DT Schema',                     │ │
│ │          │   'sender_name': 'Luca Leonardo Scorcia',                                                   │ │
│ │          │   'sender_email': '[email protected]',                                                    │ │
│ │          │   'sent_at': '2026-06-15T20:57:50+02:00',                                                   │ │
│ │          │   'added_at': '2026-06-16T10:58:10.738093+00:00',                                           │ │
│ │          │   'status': 'new',                                                                          │ │
│ │          │   'num_patches': 1,                                                                         │ │
│ │          │   ... +10                                                                                   │ │
│ │          }                                                                                             │ │
│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                            │
│ /home/broonie/git/b4/src/b4/review/_review.py:221 in retrieve_series_messages                              │
│                                                                                                            │
│    218 │                                                                                                   │
│    219 │   if not message_id:                                                                              │
│    220 │   │   raise LookupError('No message-id for this series')                                          │
│ ❱  221 │   return _retrieve_messages(message_id)                                                           │
│    222                                                                                                     │
│    223                                                                                                     │
│    224 def _get_lore_series(                                                                               │
│                                                                                                            │
│ ╭──────────────────────────────────────────────── locals ────────────────────────────────────────────────╮ │
│ │     change_id = '20260615-asoc-dt-bindings-mtk-btcvsd-snd-convert-to-dt-schema-611f77a4d16d'           │ │
│ │    identifier = 'apply'                                                                                │ │
│ │ is_rethreaded = False                                                                                  │ │
│ │    message_id = '[email protected]'                                           │ │
│ │      revision = 4                                                                                      │ │
│ │        series = {                                                                                      │ │
│ │                 │   'track_id': 718,                                                                   │ │
│ │                 │   'change_id':                                                                       │ │
│ │                 '20260615-asoc-dt-bindings-mtk-btcvsd-snd-convert-to-dt-schema-611f77a4d16d',          │ │
│ │                 │   'revision': 4,                                                                     │ │
│ │                 │   'subject': 'ASoC: dt-bindings: mtk-btcvsd-snd: Convert to DT Schema',              │ │
│ │                 │   'sender_name': 'Luca Leonardo Scorcia',                                            │ │
│ │                 │   'sender_email': '[email protected]',                                             │ │
│ │                 │   'sent_at': '2026-06-15T20:57:50+02:00',                                            │ │
│ │                 │   'added_at': '2026-06-16T10:58:10.738093+00:00',                                    │ │
│ │                 │   'status': 'new',                                                                   │ │
│ │                 │   'num_patches': 1,                                                                  │ │
│ │                 │   ... +10                                                                            │ │
│ │                 }                                                                                      │ │
│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                            │
│ /home/broonie/git/b4/src/b4/review/_review.py:183 in _retrieve_messages                                    │
│                                                                                                            │
│    180 │   │   wantname=None,                                                                              │
│    181 │   │   wantver=None,                                                                               │
│    182 │   )                                                                                               │
│ ❱  183 │   _ret_msgid, msgs = b4.retrieve_messages(cmdargs)                                                │
│    184 │   if not msgs:                                                                                    │
│    185 │   │   raise LookupError(f'Could not retrieve messages for {message_id}')                          │
│    186 │   return msgs                                                                                     │
│                                                                                                            │
│ ╭──────────────────────────────────────────────── locals ────────────────────────────────────────────────╮ │
│ │    cmdargs = Namespace(msgid='[email protected]', localmbox=None,             │ │
│ │              nocache=True, noparent=False, wantname=None, wantver=None)                                │ │
│ │ message_id = '[email protected]'                                              │ │
│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                            │
│ /home/broonie/git/b4/src/b4/__init__.py:5469 in retrieve_messages                                          │
│                                                                                                            │
│   5466 │   │   │   # Just that msgid, please                                                               │
│   5467 │   │   │   pickings.add(msgid)                                                                     │
│   5468 │   │   msgs = (                                                                                    │
│ ❱ 5469 │   │   │   get_pi_thread_by_msgid(                                                                 │
│   5470 │   │   │   │   msgid,                                                                              │
│   5471 │   │   │   │   nocache=cmdargs.nocache,                                                            │
│   5472 │   │   │   │   onlymsgids=pickings,                                                                │
│                                                                                                            │
│ ╭──────────────────────────────────────────────── locals ────────────────────────────────────────────────╮ │
│ │     cmdargs = Namespace(msgid='[email protected]', localmbox=None,            │ │
│ │               nocache=True, noparent=False, wantname=None, wantver=None)                               │ │
│ │       msgid = '[email protected]'                                             │ │
│ │    pickings = set()                                                                                    │ │
│ │ with_thread = True                                                                                     │ │
│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                            │
│ /home/broonie/git/b4/src/b4/__init__.py:4306 in get_pi_thread_by_msgid                                     │
│                                                                                                            │
│   4303 │   │   logger.info('Looking up %s', msgid)                                                         │
│   4304 │   node = get_lore_node()                                                                          │
│   4305 │   try:                                                                                            │
│ ❱ 4306 │   │   t_mbox = node.get_mbox_by_msgid(msgid, nocache=nocache)                                     │
│   4307 │   except liblore.RemoteError as ex:                                                               │
│   4308 │   │   if not quiet:                                                                               │
│   4309 │   │   │   logger.critical('Could not retrieve thread: %s', ex)                                    │
│                                                                                                            │
│ ╭──────────────────────────── locals ────────────────────────────╮                                         │
│ │       msgid = '[email protected]'     │                                         │
│ │     nocache = True                                             │                                         │
│ │        node = <liblore.node.LoreNode object at 0x7fb5c2998ad0> │                                         │
│ │  onlymsgids = set()                                            │                                         │
│ │       quiet = False                                            │                                         │
│ │ with_thread = True                                             │                                         │
│ ╰────────────────────────────────────────────────────────────────╯                                         │
│                                                                                                            │
│ /home/broonie/git/b4/.venv/lib/python3.13/site-packages/liblore/node.py:837 in get_mbox_by_msgid           │
│                                                                                                            │
│    834 │   │                                                                                               │
│    835 │   │   qmsgid = urllib.parse.quote_plus(msgid)                                                     │
│    836 │   │   mbox_url = f'{self._url}/{qmsgid}/t.mbox.gz'                                                │
│ ❱  837 │   │   resp = self._request('GET', mbox_url)                                                       │
│    838 │   │   if resp.status_code == 404:                                                                 │
│    839 │   │   │   # The message may live under a different list path.  Try a                              │
│    840 │   │   │   # HEAD against the bare origin and follow redirects to                                  │
│                                                                                                            │
│ ╭──────────────────────────────────────────── locals ─────────────────────────────────────────────╮        │
│ │      key = 'a36d75cecaf9499f5c9bc0815c0aedb3f079ffe4b18f8ffe978c45677ef724d3'                   │        │
│ │ mbox_url = 'https://lore.kernel.org/all/20260615185810.11804-1-l.scorcia%40gmail.com/t.mbox.'+2 │        │
│ │    msgid = '[email protected]'                                         │        │
│ │  nocache = True                                                                                 │        │
│ │   qmsgid = '20260615185810.11804-1-l.scorcia%40gmail.com'                                       │        │
│ │     self = <liblore.node.LoreNode object at 0x7fb5c2998ad0>                                     │        │
│ ╰─────────────────────────────────────────────────────────────────────────────────────────────────╯        │
│                                                                                                            │
│ /home/broonie/git/b4/.venv/lib/python3.13/site-packages/liblore/node.py:686 in _request                    │
│                                                                                                            │
│    683 │   │                                                                                               │
│    684 │   │   for origin in self._all_origins:                                                            │
│    685 │   │   │   if self._cancel_event.is_set():                                                         │
│ ❱  686 │   │   │   │   raise OperationCancelledError('Request cancelled')                                  │
│    687 │   │   │   request_url = self._rewrite_url(url, origin)                                            │
│    688 │   │   │   logger.debug('Trying %s %s', method, request_url)                                       │
│    689 │   │   │   try:                                                                                    │
│                                                                                                            │
│ ╭─────────────────────────────────────────────── locals ────────────────────────────────────────────────╮  │
│ │         kwargs = {'timeout': (5.0, 30.0)}                                                             │  │
│ │       last_exc = None                                                                                 │  │
│ │      last_resp = None                                                                                 │  │
│ │         method = 'GET'                                                                                │  │
│ │         origin = 'https://lore.kernel.org'                                                            │  │
│ │ raise_on_error = True                                                                                 │  │
│ │           self = <liblore.node.LoreNode object at 0x7fb5c2998ad0>                                     │  │
│ │        session = <requests.sessions.Session object at 0x7fb5c2074b90>                                 │  │
│ │            url = 'https://lore.kernel.org/all/20260615185810.11804-1-l.scorcia%40gmail.com/t.mbox.'+2 │  │
│ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯  │
╰────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
OperationCancelledError: Request cancelled

Thanks,
Mark
signature.asc (application/pgp-signature, 488 B)
-----BEGIN PGP SIGNATURE-----

iQEzBAABCgAdFiEEreZoqmdXGLWf4p/qJNaLcl1Uh9AFAmoxPEAACgkQJNaLcl1U
h9B86gf/c0/73IrYBw2U9W1X0Sf0eqhj8gmO0sr+yuCgplJmzcSKCBh9u/mqzt26
fWJNRSxk9+W8LAvoXWvtXxRSG74onXd08LPg/WXkQRdo2qxdtKSge5FWINggnls7
XvUvQfBBWgsgsD/n+Kls/0y1SOJRB5RY/1/sR/7opBZJPlXUC3DBZVI/AkJI5qih
6C+QxKM/rYoSk/3iROhSD6QK3DHylwkOg+mQ86LaGJ6SnxlwHSzBWcbuF4IZGijd
Jq+OLAe8VLAPNMYK0GDufs92qLrHEb7ouxCJ3LaaEj/PTp+9LTkcPqDcqDVlAfMm
4bYvdgLUSltZS2frymEKmIV3f6tMLw==
=+eUS
-----END PGP SIGNATURE-----