Skip to content

Apply Configuration hangs forever when run without a TTY (cli_progress urwid loop never exits) #5479

Description

@tyu543

Describe the bug

Clicking Apply Configuration in the panel (or running apply_configs.sh non-interactively, e.g. over a non-interactive SSH or from cron) hangs
indefinitely
. The actual config regeneration finishes within ~1 minute (config files get rewritten, services restart), but the wrapper process never exits and
keeps burning CPU (observed 20+ minutes).

The practical impact is severe: because Apply never completes, panel/DB changes are never pushed to the running server config. Users edit settings in the GUI,
the DB is updated, but the live xray/haproxy configs stay stale — so every change silently has no effect until the stuck process is killed manually.

Root cause

common/utils.shshow_progress_window() always wraps the command with an urwid full-screen TUI:

function show_progress_window() {
    ...
    python -m cli_progress --title "Hiddify Manager" "$@"
    ...
}

cli_progress (hiddify/cli_progress) uses an urwid MainLoop. When there is no TTY (panel background invocation, cron, non-interactive SSH), the urwid event loop does
not exit after the wrapped subprocess finishes — it spins forever. The log also shows tput: No value for $TERM and no -T specified. The timeout that wraps it only
kills the outer shell; the python -m cli_progress ... ./install.sh apply_configs child gets reparented to init and keeps running.

Steps to reproduce

1. On the server run: bash /opt/hiddify-manager/apply_configs.sh over a non-interactive SSH (no PTY), or trigger Apply Configuration from the web panel.
2. Observe it never returns. pgrep -af cli_progress shows the process still running (high CPU) long after the config files' mtime has already updated.

Environment

- Hiddify Manager: 12.3.3
- Python: 3.13 (.venv)
- Invocation chain: web panel / apply_configs.sh → install.sh apply_configs → show_progress_window() (common/utils.sh) → python -m cli_progress

Proposed fix

Detect "no TTY" and bypass the urwid UI, running the command directly. Either:

Option A — in cli_progress/__main__.py main():
def main():
    args = parse_arguments()
    import sys, subprocess
    if not sys.stdout.isatty():
        raise SystemExit(subprocess.call(args.command))
    ui = ProgressUI(args.log, args.command, args.title, args.subtitle, args.regex)
    ui.start()

Option B — in common/utils.sh show_progress_window(): if stdout is not a TTY ([ ! -t 1 ]), run the command directly instead of wrapping it with cli_progress.

I verified Option A on a live 12.3.3 install: Apply now completes in ~2.5 min and exits cleanly, while interactive terminal runs still show the progress UI.

Workaround for others hitting this now

After triggering Apply, once the config files' mtime updates (~1 min), kill the stuck process: pkill -9 -f cli_progress. The config regeneration itself has already
succeeded by then.

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