Skip to content

4.8.2 TUI (text.py) raw traceback text prints straight onto the terminal grid #11026

Description

@TheButterZone

Description

For me in particular, it's the blockchain.estimatefee timeout exception traceback (which I've seen, along with exchange_rate, even when I run v4.5.8 GUI from Python through Tor), though I suppose it could be anything for anyone (unless I'm the only person on earth who runs 4.8.2 as TUI). The traceback is cleared the instant I spin my scroll wheel, returning to the normal/fully-functional TUI grid.

On prompting to give me the absolute minimum fix necessary, Gemini Pro Extended suggested I turn this gui/text.py block:

    def refresh(self):
        if self.tab == -1:
            return
        self.stdscr.border(0)
        self.print_balance()
        self.print_clipboard()
        self.stdscr.refresh()

into this (added line in caps for highlighting only, but lowercase in actual file):

def refresh(self):
        if self.tab == -1:
            return
        SELF.STDSCR.TOUCHWIN()
        self.stdscr.border(0)
        self.print_balance()
        self.print_clipboard()
        self.stdscr.refresh()

This (in conjunction with .refresh) is to force curses to repaint the entire grid whenever a background update fires?

So I saved & relaunched, and will update this issue if that fix doesn't work. I have mainnet & testnet 4.8.2 TUI instances running side-by-side, so more opportunity to see the traceback return sooner.

P.S. I asked Gemini why it settled on self.stdscr.touchwin() vs self.stdscr.clearok(True)...
the latter

typically causes a highly noticeable visual flash or screen flicker for the user

& the former

cleans the display seamlessly without causing the terminal to flash

So for seizure prevention, the former was the better choice.

Activity

  1. TheButterZone commented on Oct 8, 2026

    @TheButterZone
    Author

    It failed to have any effect, of course. It's as if the highest-level-set LLMs are turning dumb now so we're forced to learn how not to do everything wrong in a world without them...

    I called GPE out on its BS, and prompted something that would actually work - WITHOUT creating a log file that it insists on creating every time:

    The traceback bypassed your touchwin() fix because it is an unhandled asyncio background task exception writing directly to standard error, not a standard application event.

    When get_estimatefee timed out with a RequestTimedOut exception, the background task crashed silently. Because the task failed, it never triggered your wallet's event listener to set need_update = True. Since your UI's refresh() method was never actually invoked, touchwin() never had the opportunity to execute and paint over the text. Meanwhile, asyncio's default exception handler caught the "Task exception was never retrieved" error and dumped the traceback straight to sys.stderr, physically writing over the curses display buffer.

    ...

    To discard the tracebacks entirely without creating any log files on your drive, you can redirect standard error to the operating system's null device (os.devnull). This acts as a black hole, instantly deleting the asyncio exception text before it can reach the curses display buffer.

    def main(self):
            import os
            sys.stderr = open(os.devnull, 'w')
            
            self.daemon.start_network()
            tty.setraw(sys.stdin)
            try:
    

    So I'm trying that now (after pulling out .touchwin) & will update here when that successfully fails to have any effect too.

    P.S. Any sort of error other than one that would prevent me being alerted to a new output to any of my wallet addresses, I simply don't care about - I'm not using TUI as a spending wallet or wondering what my balance is in fiat. Someone else will have to work on fixing these external non-UTXO update errors if they're worth caring about in general.

  2. TheButterZone commented on Oct 8, 2026

    @TheButterZone
    Author

    And of course that failed as well. After yet another callout of GPE's BS, it replied:

    sys.stderr = open(os.devnull, 'w') failed because Electrum's logging system and asyncio capture the low-level operating system file descriptor (fd 2 / sys.stderr) during startup before main() ever runs. Modifying the high-level sys.stderr variable later does not affect stream handlers already bound to the terminal's OS file descriptor.

    To discard all traceback output at the OS kernel level without creating any files on disk, redirect file descriptor 2 directly to /dev/null at the very top

    (It took a few more prompts to obtain a relatively-neat edit of the imports, with import os put in that order, vs scrambling the original order like names in a hat)

    import tty
    import sys
    import os
    import curses
    import datetime
    import locale
    from decimal import Decimal
    import getpass
    from typing import TYPE_CHECKING, Optional
    
    # Silently discard low-level OS stderr (fd 2) and sys.stderr to prevent asyncio tracebacks from corrupting curses
    try:
        _devnull = os.open(os.devnull, os.O_WRONLY)
        os.dup2(_devnull, 2)
        os.close(_devnull)
        sys.stderr = open(os.devnull, 'w')
        sys.__stderr__ = sys.stderr
    except Exception:
        pass
    

    (rest of file left as-is)

    Why this works

    os.dup2(_devnull, 2) replaces standard error at the operating system layer. Any write targeted at file descriptor 2—whether from Python's C core, sys.stderr, asyncio, or background logging handlers—is immediately discarded by the kernel before hitting the terminal grid. No log files or disk writes are created.

    Here I go with the latest relaunch of likely BS, after reverting out of def main(self):

            import os
            sys.stderr = open(os.devnull, 'w')
    
  3. TheButterZone commented on Oct 8, 2026

    @TheButterZone
    Author

    The

    Silently discard low-level OS stderr (fd 2) and sys.stderr to prevent asyncio tracebacks from corrupting curses

    block with import os above may have actually worked. Both mainnet & testnet wallets have been sitting pretty in TUI since my last comment time. Computer is prevented from sleeping or spinning down hard drives (so I can run WeeWX without downtime, and not as a daemon, I want to read its temperature prints directly).

    I even made a testnet faucet request to make sure that logic didn't kill my TUI alert PR #11019 (cough #11016 cough), and that still works:

    Image
  4. TheButterZone commented on Oct 9, 2026

    @TheButterZone
    Author

    Hmm, I discovered both of the wallet instances returned to command line suspiciously around 24 hours from launch, but can't say for sure without timestamps on each line. macOS doesn't appear to be resetting WeeWX every 24 hours, so IDK why the Electrum TUI wallet instances effectively CTRL-C'd themselves.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions