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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions