hermes - ✅(Solved) Fix [Bug]: Installer setup wizard fails in Docker builds: [ -e /dev/tty ] check unreliable [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#16746Fetched 2026-04-28 06:51:02
View on GitHub
Comments
0
Participants
1
Timeline
6
Reactions
0
Author
Participants
Timeline (top)
labeled ×4cross-referenced ×1renamed ×1

The Hermes installer (scripts/install.sh) tries to skip its interactive setup wizard when no terminal is available, using if ! [ -e /dev/tty ] at line 1333. In a Docker build context, /dev/tty typically exists as a device node (-e returns true), but opening it fails with ENXIO: No such device or address. The skip never triggers, the wizard runs, and the build crashes when it tries to read from /dev/tty.

Error Message

docker build -t hermes-test .

...

-> Starting setup wizard...

/tmp/install.sh: line 1347: /dev/tty: No such device or address

ERROR: failed to build

Root Cause

Root Cause Analysis (optional)

Fix Action

Workaround

The installer accepts --skip-setup, which the wizard handler honors before reaching the /dev/tty check (line 1326). Adding it to the Docker RUN invocation works:

RUN curl -fsSL https://.../install.sh -o /tmp/install.sh
&& bash /tmp/install.sh --skip-setup

This skips the wizard cleanly, and configuration can be supplied later via a mounted ~/.hermes/ volume or hermes setup interactively after docker run.

PR fix notes

PR #16750: fix(install): probe /dev/tty by opening it, not bare existence (#16746)

Description (problem / solution / changelog)

Summary

  • Replace [ -e /dev/tty ] with an open-based probe in run_setup_wizard() so Docker / CI installs skip the wizard cleanly instead of crashing on the next < /dev/tty redirect.
  • Add a static regression test that asserts the gate does not use the bare existence check and that the open-based probe is in place.

The bug

On a Docker build, /dev/tty exists as a device node (so [ -e /dev/tty ] returns true), but opening it fails with ENXIO: No such device or address. The skip at the top of run_setup_wizard() never triggered, the wizard proceeded, and bash aborted a few lines later when it tried to redirect from /dev/tty:

/tmp/install.sh: line 1347: /dev/tty: No such device or address

The --skip-setup flag works around this, but the bare existence check was supposed to catch this case automatically.

The fix

(: </dev/tty) 2>/dev/null runs the no-op : in a subshell with stdin redirected from /dev/tty. The subshell exits non-zero exactly when opening /dev/tty would fail (ENXIO in Docker, ENODEV in some CI sandboxes), and the existing skip path runs. On a real terminal — including the curl | bash flow the wizard targets — the open succeeds and the wizard runs as before. The subshell isolates the probe so we don't leave an inherited fd open in the script.

scripts/install.sh has two other [ -e /dev/tty ] sites (the apt sudo prompt fallback near line 732 and the gateway-install gate near line 1395). They have the same shape but are outside the reproduction path in #16746 and on a separate code path, so this PR keeps the diff scoped to the wizard gate the reporter actually hit. Happy to follow up with a second PR for those if maintainers prefer.

Test plan

  • Focused regression test: uv run --with pytest --with pytest-xdist python3 -m pytest tests/test_install_sh_setup_wizard_tty_probe.py -v — both assertions pass on this branch.
  • Regression guard: same suite run with scripts/install.sh reverted to origin/main fails with the documented assertion message; restoring the fix passes.
  • Adjacent: tests/hermes_cli/test_managed_installs.py and tests/hermes_cli/test_doctor_command_install.py — only baseline failure is test_windows_skips_check, which reproduces on clean origin/main (it depends on _winapi, macOS lacks the module).
  • Manual probe behavior:
    • bash -c '(: </dev/tty) 2>/dev/null && echo OK || echo FAIL' < /dev/nullFAIL (matches Docker-build skip).
    • On a real interactive terminal the same probe returns OK — the wizard still runs as before.

Related

  • Fixes #16746.

Changed files

  • scripts/install.sh (modified, +6/-1)
  • tests/test_install_sh_setup_wizard_tty_probe.py (added, +75/-0)

Code Example

# The setup wizard reads from /dev/tty, so it works even when the
# install script itself is piped (curl | bash). Only skip if no
# terminal is available at all (e.g. Docker build, CI).
if ! [ -e /dev/tty ]; then
    log_info "Setup wizard skipped (no terminal available). Run 'hermes setup' after install."
    return 0
fi

---

"$INSTALL_DIR/venv/bin/python" -m hermes_cli.main setup < /dev/tty

---

/tmp/hermes-install.sh: line 1347: /dev/tty: No such device or address

---

FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl ca-certificates git bash xz-utils
RUN curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh \
      -o /tmp/install.sh \
 && bash /tmp/install.sh

---

docker build -t hermes-test .
# ...
# -> Starting setup wizard...
# /tmp/install.sh: line 1347: /dev/tty: No such device or address
# ERROR: failed to build

---

RUN curl -fsSL https://.../install.sh -o /tmp/install.sh \
 && bash /tmp/install.sh --skip-setup

---

if ! { exec 3</dev/tty; } 2>/dev/null; then
    log_info "Setup wizard skipped (no terminal available)."
    return 0
fi
exec 3<&-

---

# The setup wizard reads from /dev/tty, so it works even when the
# install script itself is piped (curl | bash). Only skip if no
# terminal is available at all (e.g. Docker build, CI).
if ! [ -e /dev/tty ]; then
    log_info "Setup wizard skipped (no terminal available). Run 'hermes setup' after install."
    return 0
fi

---

"$INSTALL_DIR/venv/bin/python" -m hermes_cli.main setup < /dev/tty

---

/tmp/hermes-install.sh: line 1347: /dev/tty: No such device or address

---

FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl ca-certificates git bash xz-utils
RUN curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh \
      -o /tmp/install.sh \
 && bash /tmp/install.sh

---

docker build -t hermes-test .
# ...
# -> Starting setup wizard...
# /tmp/install.sh: line 1347: /dev/tty: No such device or address
# ERROR: failed to build

---

RUN curl -fsSL https://.../install.sh -o /tmp/install.sh \
 && bash /tmp/install.sh --skip-setup

---

if ! { exec 3</dev/tty; } 2>/dev/null; then
    log_info "Setup wizard skipped (no terminal available)."
    return 0
fi
exec 3<&-

---

# The setup wizard reads from /dev/tty, so it works even when the
# install script itself is piped (curl | bash). Only skip if no
# terminal is available at all (e.g. Docker build, CI).
if ! [ -e /dev/tty ]; then
    log_info "Setup wizard skipped (no terminal available). Run 'hermes setup' after install."
    return 0
fi

---

"$INSTALL_DIR/venv/bin/python" -m hermes_cli.main setup < /dev/tty

---

/tmp/hermes-install.sh: line 1347: /dev/tty: No such device or address

---

FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl ca-certificates git bash xz-utils
RUN curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh \
      -o /tmp/install.sh \
 && bash /tmp/install.sh

---

docker build -t hermes-test .
# ...
# -> Starting setup wizard...
# /tmp/install.sh: line 1347: /dev/tty: No such device or address
# ERROR: failed to build

---

RUN curl -fsSL https://.../install.sh -o /tmp/install.sh \
 && bash /tmp/install.sh --skip-setup

---

if ! { exec 3</dev/tty; } 2>/dev/null; then
    log_info "Setup wizard skipped (no terminal available)."
    return 0
fi
exec 3<&-

---

# The setup wizard reads from /dev/tty, so it works even when the
# install script itself is piped (curl | bash). Only skip if no
# terminal is available at all (e.g. Docker build, CI).
if ! [ -e /dev/tty ]; then
    log_info "Setup wizard skipped (no terminal available). Run 'hermes setup' after install."
    return 0
fi

---

"$INSTALL_DIR/venv/bin/python" -m hermes_cli.main setup < /dev/tty

---

/tmp/hermes-install.sh: line 1347: /dev/tty: No such device or address

---

FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl ca-certificates git bash xz-utils
RUN curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh \
      -o /tmp/install.sh \
 && bash /tmp/install.sh

---

docker build -t hermes-test .
# ...
# -> Starting setup wizard...
# /tmp/install.sh: line 1347: /dev/tty: No such device or address
# ERROR: failed to build

---

RUN curl -fsSL https://.../install.sh -o /tmp/install.sh \
 && bash /tmp/install.sh --skip-setup

---

if ! { exec 3</dev/tty; } 2>/dev/null; then
    log_info "Setup wizard skipped (no terminal available)."
    return 0
fi
exec 3<&-

---

Installer line 1333 `[ -e /dev/tty ]` unreliable in Docker builds

## Summary

The Hermes installer (`scripts/install.sh`) tries to skip its interactive setup wizard when no terminal is available, using `if ! [ -e /dev/tty ]` at line 1333. In a Docker build context, `/dev/tty` typically exists as a device node (`-e` returns true), but opening it fails with `ENXIO: No such device or address`. The skip never triggers, the wizard runs, and the build crashes when it tries to read from `/dev/tty`.

## What I expected

When run in a non-interactive context (Docker build, CI), the installer should detect that and skip the setup wizard automatically — which is exactly what the comment at line 1330 promises:


# The setup wizard reads from /dev/tty, so it works even when the
# install script itself is piped (curl | bash). Only skip if no
# terminal is available at all (e.g. Docker build, CI).
if ! [ -e /dev/tty ]; then
    log_info "Setup wizard skipped (no terminal available). Run 'hermes setup' after install."
    return 0
fi


## What actually happens

In a Docker build, `[ -e /dev/tty ]` returns true (the device node exists in the namespace), so the function continues past the skip. Then at line 1347:


"$INSTALL_DIR/venv/bin/python" -m hermes_cli.main setup < /dev/tty


The redirect fails:


/tmp/hermes-install.sh: line 1347: /dev/tty: No such device or address


The build aborts.

## Reproduction

Minimal `Dockerfile`:


FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl ca-certificates git bash xz-utils
RUN curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh \
      -o /tmp/install.sh \
 && bash /tmp/install.sh



docker build -t hermes-test .
# ...
# -> Starting setup wizard...
# /tmp/install.sh: line 1347: /dev/tty: No such device or address
# ERROR: failed to build


## Environment

- Hermes Agent v0.11.0 (2026.4.23) installer
- Base image: `python:3.11-slim` (Debian Trixie at time of report)
- Docker build context (BuildKit and legacy builder both reproduce)

## Workaround

The installer accepts `--skip-setup`, which the wizard handler honors before reaching the `/dev/tty` check (line 1326). Adding it to the Docker `RUN` invocation works:


RUN curl -fsSL https://.../install.sh -o /tmp/install.sh \
 && bash /tmp/install.sh --skip-setup


This skips the wizard cleanly, and configuration can be supplied later via a mounted `~/.hermes/` volume or `hermes setup` interactively after `docker run`.

## Suggested fix

`[ -e /dev/tty ]` is not a reliable test for "is a terminal usable". A more robust test would actually attempt to open it:


if ! { exec 3</dev/tty; } 2>/dev/null; then
    log_info "Setup wizard skipped (no terminal available)."
    return 0
fi
exec 3<&-


Or rely on `[ -t 0 ]` if stdin is the intended interactive channel. Either change makes Docker / CI installs work without the explicit `--skip-setup` workaround, which would be more discoverable for first-time users (the current behavior produces a confusing error message at the very end of an otherwise-successful 60+-second install).

---
RAW_BUFFERClick to expand / collapse

Bug Description

Installer line 1333 [ -e /dev/tty ] unreliable in Docker builds

Summary

The Hermes installer (scripts/install.sh) tries to skip its interactive setup wizard when no terminal is available, using if ! [ -e /dev/tty ] at line 1333. In a Docker build context, /dev/tty typically exists as a device node (-e returns true), but opening it fails with ENXIO: No such device or address. The skip never triggers, the wizard runs, and the build crashes when it tries to read from /dev/tty.

What I expected

When run in a non-interactive context (Docker build, CI), the installer should detect that and skip the setup wizard automatically — which is exactly what the comment at line 1330 promises:

# The setup wizard reads from /dev/tty, so it works even when the
# install script itself is piped (curl | bash). Only skip if no
# terminal is available at all (e.g. Docker build, CI).
if ! [ -e /dev/tty ]; then
    log_info "Setup wizard skipped (no terminal available). Run 'hermes setup' after install."
    return 0
fi

What actually happens

In a Docker build, [ -e /dev/tty ] returns true (the device node exists in the namespace), so the function continues past the skip. Then at line 1347:

"$INSTALL_DIR/venv/bin/python" -m hermes_cli.main setup < /dev/tty

The redirect fails:

/tmp/hermes-install.sh: line 1347: /dev/tty: No such device or address

The build aborts.

Reproduction

Minimal Dockerfile:

FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl ca-certificates git bash xz-utils
RUN curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh \
      -o /tmp/install.sh \
 && bash /tmp/install.sh
docker build -t hermes-test .
# ...
# -> Starting setup wizard...
# /tmp/install.sh: line 1347: /dev/tty: No such device or address
# ERROR: failed to build

Environment

  • Hermes Agent v0.11.0 (2026.4.23) installer
  • Base image: python:3.11-slim (Debian Trixie at time of report)
  • Docker build context (BuildKit and legacy builder both reproduce)

Workaround

The installer accepts --skip-setup, which the wizard handler honors before reaching the /dev/tty check (line 1326). Adding it to the Docker RUN invocation works:

RUN curl -fsSL https://.../install.sh -o /tmp/install.sh \
 && bash /tmp/install.sh --skip-setup

This skips the wizard cleanly, and configuration can be supplied later via a mounted ~/.hermes/ volume or hermes setup interactively after docker run.

Suggested fix

[ -e /dev/tty ] is not a reliable test for "is a terminal usable". A more robust test would actually attempt to open it:

if ! { exec 3</dev/tty; } 2>/dev/null; then
    log_info "Setup wizard skipped (no terminal available)."
    return 0
fi
exec 3<&-

Or rely on [ -t 0 ] if stdin is the intended interactive channel. Either change makes Docker / CI installs work without the explicit --skip-setup workaround, which would be more discoverable for first-time users (the current behavior produces a confusing error message at the very end of an otherwise-successful 60+-second install). maxidijk@Mac craftbot-mail-triage %

Steps to Reproduce

Installer line 1333 [ -e /dev/tty ] unreliable in Docker builds

Summary

The Hermes installer (scripts/install.sh) tries to skip its interactive setup wizard when no terminal is available, using if ! [ -e /dev/tty ] at line 1333. In a Docker build context, /dev/tty typically exists as a device node (-e returns true), but opening it fails with ENXIO: No such device or address. The skip never triggers, the wizard runs, and the build crashes when it tries to read from /dev/tty.

What I expected

When run in a non-interactive context (Docker build, CI), the installer should detect that and skip the setup wizard automatically — which is exactly what the comment at line 1330 promises:

# The setup wizard reads from /dev/tty, so it works even when the
# install script itself is piped (curl | bash). Only skip if no
# terminal is available at all (e.g. Docker build, CI).
if ! [ -e /dev/tty ]; then
    log_info "Setup wizard skipped (no terminal available). Run 'hermes setup' after install."
    return 0
fi

What actually happens

In a Docker build, [ -e /dev/tty ] returns true (the device node exists in the namespace), so the function continues past the skip. Then at line 1347:

"$INSTALL_DIR/venv/bin/python" -m hermes_cli.main setup < /dev/tty

The redirect fails:

/tmp/hermes-install.sh: line 1347: /dev/tty: No such device or address

The build aborts.

Reproduction

Minimal Dockerfile:

FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl ca-certificates git bash xz-utils
RUN curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh \
      -o /tmp/install.sh \
 && bash /tmp/install.sh
docker build -t hermes-test .
# ...
# -> Starting setup wizard...
# /tmp/install.sh: line 1347: /dev/tty: No such device or address
# ERROR: failed to build

Environment

  • Hermes Agent v0.11.0 (2026.4.23) installer
  • Base image: python:3.11-slim (Debian Trixie at time of report)
  • Docker build context (BuildKit and legacy builder both reproduce)

Workaround

The installer accepts --skip-setup, which the wizard handler honors before reaching the /dev/tty check (line 1326). Adding it to the Docker RUN invocation works:

RUN curl -fsSL https://.../install.sh -o /tmp/install.sh \
 && bash /tmp/install.sh --skip-setup

This skips the wizard cleanly, and configuration can be supplied later via a mounted ~/.hermes/ volume or hermes setup interactively after docker run.

Suggested fix

[ -e /dev/tty ] is not a reliable test for "is a terminal usable". A more robust test would actually attempt to open it:

if ! { exec 3</dev/tty; } 2>/dev/null; then
    log_info "Setup wizard skipped (no terminal available)."
    return 0
fi
exec 3<&-

Or rely on [ -t 0 ] if stdin is the intended interactive channel. Either change makes Docker / CI installs work without the explicit --skip-setup workaround, which would be more discoverable for first-time users (the current behavior produces a confusing error message at the very end of an otherwise-successful 60+-second install).

Expected Behavior

Installer line 1333 [ -e /dev/tty ] unreliable in Docker builds

Summary

The Hermes installer (scripts/install.sh) tries to skip its interactive setup wizard when no terminal is available, using if ! [ -e /dev/tty ] at line 1333. In a Docker build context, /dev/tty typically exists as a device node (-e returns true), but opening it fails with ENXIO: No such device or address. The skip never triggers, the wizard runs, and the build crashes when it tries to read from /dev/tty.

What I expected

When run in a non-interactive context (Docker build, CI), the installer should detect that and skip the setup wizard automatically — which is exactly what the comment at line 1330 promises:

# The setup wizard reads from /dev/tty, so it works even when the
# install script itself is piped (curl | bash). Only skip if no
# terminal is available at all (e.g. Docker build, CI).
if ! [ -e /dev/tty ]; then
    log_info "Setup wizard skipped (no terminal available). Run 'hermes setup' after install."
    return 0
fi

What actually happens

In a Docker build, [ -e /dev/tty ] returns true (the device node exists in the namespace), so the function continues past the skip. Then at line 1347:

"$INSTALL_DIR/venv/bin/python" -m hermes_cli.main setup < /dev/tty

The redirect fails:

/tmp/hermes-install.sh: line 1347: /dev/tty: No such device or address

The build aborts.

Reproduction

Minimal Dockerfile:

FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl ca-certificates git bash xz-utils
RUN curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh \
      -o /tmp/install.sh \
 && bash /tmp/install.sh
docker build -t hermes-test .
# ...
# -> Starting setup wizard...
# /tmp/install.sh: line 1347: /dev/tty: No such device or address
# ERROR: failed to build

Environment

  • Hermes Agent v0.11.0 (2026.4.23) installer
  • Base image: python:3.11-slim (Debian Trixie at time of report)
  • Docker build context (BuildKit and legacy builder both reproduce)

Workaround

The installer accepts --skip-setup, which the wizard handler honors before reaching the /dev/tty check (line 1326). Adding it to the Docker RUN invocation works:

RUN curl -fsSL https://.../install.sh -o /tmp/install.sh \
 && bash /tmp/install.sh --skip-setup

This skips the wizard cleanly, and configuration can be supplied later via a mounted ~/.hermes/ volume or hermes setup interactively after docker run.

Suggested fix

[ -e /dev/tty ] is not a reliable test for "is a terminal usable". A more robust test would actually attempt to open it:

if ! { exec 3</dev/tty; } 2>/dev/null; then
    log_info "Setup wizard skipped (no terminal available)."
    return 0
fi
exec 3<&-

Or rely on [ -t 0 ] if stdin is the intended interactive channel. Either change makes Docker / CI installs work without the explicit --skip-setup workaround, which would be more discoverable for first-time users (the current behavior produces a confusing error message at the very end of an otherwise-successful 60+-second install).

Actual Behavior

Installer line 1333 [ -e /dev/tty ] unreliable in Docker builds

Summary

The Hermes installer (scripts/install.sh) tries to skip its interactive setup wizard when no terminal is available, using if ! [ -e /dev/tty ] at line 1333. In a Docker build context, /dev/tty typically exists as a device node (-e returns true), but opening it fails with ENXIO: No such device or address. The skip never triggers, the wizard runs, and the build crashes when it tries to read from /dev/tty.

What I expected

When run in a non-interactive context (Docker build, CI), the installer should detect that and skip the setup wizard automatically — which is exactly what the comment at line 1330 promises:

# The setup wizard reads from /dev/tty, so it works even when the
# install script itself is piped (curl | bash). Only skip if no
# terminal is available at all (e.g. Docker build, CI).
if ! [ -e /dev/tty ]; then
    log_info "Setup wizard skipped (no terminal available). Run 'hermes setup' after install."
    return 0
fi

What actually happens

In a Docker build, [ -e /dev/tty ] returns true (the device node exists in the namespace), so the function continues past the skip. Then at line 1347:

"$INSTALL_DIR/venv/bin/python" -m hermes_cli.main setup < /dev/tty

The redirect fails:

/tmp/hermes-install.sh: line 1347: /dev/tty: No such device or address

The build aborts.

Reproduction

Minimal Dockerfile:

FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl ca-certificates git bash xz-utils
RUN curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh \
      -o /tmp/install.sh \
 && bash /tmp/install.sh
docker build -t hermes-test .
# ...
# -> Starting setup wizard...
# /tmp/install.sh: line 1347: /dev/tty: No such device or address
# ERROR: failed to build

Environment

  • Hermes Agent v0.11.0 (2026.4.23) installer
  • Base image: python:3.11-slim (Debian Trixie at time of report)
  • Docker build context (BuildKit and legacy builder both reproduce)

Workaround

The installer accepts --skip-setup, which the wizard handler honors before reaching the /dev/tty check (line 1326). Adding it to the Docker RUN invocation works:

RUN curl -fsSL https://.../install.sh -o /tmp/install.sh \
 && bash /tmp/install.sh --skip-setup

This skips the wizard cleanly, and configuration can be supplied later via a mounted ~/.hermes/ volume or hermes setup interactively after docker run.

Suggested fix

[ -e /dev/tty ] is not a reliable test for "is a terminal usable". A more robust test would actually attempt to open it:

if ! { exec 3</dev/tty; } 2>/dev/null; then
    log_info "Setup wizard skipped (no terminal available)."
    return 0
fi
exec 3<&-

Or rely on [ -t 0 ] if stdin is the intended interactive channel. Either change makes Docker / CI installs work without the explicit --skip-setup workaround, which would be more discoverable for first-time users (the current behavior produces a confusing error message at the very end of an otherwise-successful 60+-second install).

Affected Component

Setup / Installation

Messaging Platform (if gateway-related)

No response

Debug Report

Installer line 1333 `[ -e /dev/tty ]` unreliable in Docker builds

## Summary

The Hermes installer (`scripts/install.sh`) tries to skip its interactive setup wizard when no terminal is available, using `if ! [ -e /dev/tty ]` at line 1333. In a Docker build context, `/dev/tty` typically exists as a device node (`-e` returns true), but opening it fails with `ENXIO: No such device or address`. The skip never triggers, the wizard runs, and the build crashes when it tries to read from `/dev/tty`.

## What I expected

When run in a non-interactive context (Docker build, CI), the installer should detect that and skip the setup wizard automatically — which is exactly what the comment at line 1330 promises:


# The setup wizard reads from /dev/tty, so it works even when the
# install script itself is piped (curl | bash). Only skip if no
# terminal is available at all (e.g. Docker build, CI).
if ! [ -e /dev/tty ]; then
    log_info "Setup wizard skipped (no terminal available). Run 'hermes setup' after install."
    return 0
fi


## What actually happens

In a Docker build, `[ -e /dev/tty ]` returns true (the device node exists in the namespace), so the function continues past the skip. Then at line 1347:


"$INSTALL_DIR/venv/bin/python" -m hermes_cli.main setup < /dev/tty


The redirect fails:


/tmp/hermes-install.sh: line 1347: /dev/tty: No such device or address


The build aborts.

## Reproduction

Minimal `Dockerfile`:


FROM python:3.11-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
    curl ca-certificates git bash xz-utils
RUN curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh \
      -o /tmp/install.sh \
 && bash /tmp/install.sh



docker build -t hermes-test .
# ...
# -> Starting setup wizard...
# /tmp/install.sh: line 1347: /dev/tty: No such device or address
# ERROR: failed to build


## Environment

- Hermes Agent v0.11.0 (2026.4.23) installer
- Base image: `python:3.11-slim` (Debian Trixie at time of report)
- Docker build context (BuildKit and legacy builder both reproduce)

## Workaround

The installer accepts `--skip-setup`, which the wizard handler honors before reaching the `/dev/tty` check (line 1326). Adding it to the Docker `RUN` invocation works:


RUN curl -fsSL https://.../install.sh -o /tmp/install.sh \
 && bash /tmp/install.sh --skip-setup


This skips the wizard cleanly, and configuration can be supplied later via a mounted `~/.hermes/` volume or `hermes setup` interactively after `docker run`.

## Suggested fix

`[ -e /dev/tty ]` is not a reliable test for "is a terminal usable". A more robust test would actually attempt to open it:


if ! { exec 3</dev/tty; } 2>/dev/null; then
    log_info "Setup wizard skipped (no terminal available)."
    return 0
fi
exec 3<&-


Or rely on `[ -t 0 ]` if stdin is the intended interactive channel. Either change makes Docker / CI installs work without the explicit `--skip-setup` workaround, which would be more discoverable for first-time users (the current behavior produces a confusing error message at the very end of an otherwise-successful 60+-second install).

Operating System

Ubuntu 24.04

Python Version

No response

Hermes Version

No response

Additional Logs / Traceback (optional)

Root Cause Analysis (optional)

No response

Proposed Fix (optional)

No response

Are you willing to submit a PR for this?

  • I'd like to fix this myself and submit a PR

extent analysis

TL;DR

The issue can be fixed by replacing the unreliable [ -e /dev/tty ] check with a more robust test that attempts to open /dev/tty or checks if stdin is a terminal using [ -t 0 ].

Guidance

  • Replace the existing check if ! [ -e /dev/tty ]; then with if ! { exec 3</dev/tty; } 2>/dev/null; then to attempt to open /dev/tty and handle the case where it's not available.
  • Alternatively, use [ -t 0 ] to check if stdin is a terminal, which can be a more reliable indicator of an interactive environment.
  • Apply the suggested fix to the scripts/install.sh file at line 1333 to resolve the issue.
  • Verify the fix by running the Docker build with the updated installer script.

Example

if ! { exec 3</dev/tty; } 2>/dev/null; then
    log_info "Setup wizard skipped (no terminal available)."
    return 0
fi
exec 3<&-

This code attempts to open /dev/tty and skips the setup wizard if it fails.

Notes

The provided workaround of adding --skip-setup to the Docker RUN invocation can also resolve the issue, but the suggested fix provides a more robust and discoverable solution.

Recommendation

Apply the suggested fix to the scripts/install.sh file to replace the unreliable [ -e /dev/tty ] check with a more robust test. This will allow the installer to correctly detect non-interactive environments and skip the setup wizard.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix [Bug]: Installer setup wizard fails in Docker builds: [ -e /dev/tty ] check unreliable [1 pull requests, 1 participants]