Skip to content

Fix: script-default visualizers not cleared in headless mode (--headless and HEADLESS=1) - #7444

Closed
ChaitanyaParate wants to merge 1 commit into
isaac-sim:developfrom
ChaitanyaParate:chaitanya/fix-headless-visualizer-resolution
Closed

Fix: script-default visualizers not cleared in headless mode (--headless and HEADLESS=1)#7444
ChaitanyaParate wants to merge 1 commit into
isaac-sim:developfrom
ChaitanyaParate:chaitanya/fix-headless-visualizer-resolution

Conversation

@ChaitanyaParate

Copy link
Copy Markdown

Summary

Fixes a runtime crash in AppLauncher when a task script registers a default visualizer via parser.set_defaults(visualizer=["kit"]) and the process is launched in headless mode.

Fixes #7403

Root cause

_resolve_visualizer_settings incorrectly promoted set_defaults() values to explicit user intent (_cli_visualizer_explicit = True), even though the user never typed --viz on the command line.

Fix

  • Instead of unconditionally clearing visualizers (which conflicts with develop's newly supported HEADLESS=1 + Kit visualizer livestreaming lifecycle), this PR fixes the root cause directly by tracking whether the arguments came from set_defaults.
  • Added parser.set_defaults(visualizer_explicit=False) in add_app_launcher_args.
  • Updated _resolve_visualizer_settings to correctly respect visualizer_explicit=False while preserving the presence-based fallback for programmatic kwargs (e.g., AppLauncher(visualizer=["kit"])).

Testing

  • Added test_visualizer_argparse_set_defaults_is_not_explicit and test_visualizer_direct_kwargs_is_explicit to verify intent resolution logic.
  • Updated test_matrix_headless_with_viz_names_takes_precedence to align with the new develop lifecycle (expecting visualizers not to be unconditionally cleared under headless modes).

Checklist

  • Pre-commit hooks run and passed (./isaaclab.sh -f)
  • Tests added / updated
  • CHANGELOG.rst updated (via changelog fragment)

@ChaitanyaParate
ChaitanyaParate requested a review from a team August 31, 2026 05:00
@github-actions github-actions Bot added bug Something isn't working isaac-lab Related to Isaac Lab team labels Aug 31, 2026
@greptile-apps

greptile-apps Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR distinguishes script-defined argparse visualizer defaults from explicit --visualizer input so headless launches do not incorrectly enforce CLI visualizer behavior.

  • Adds a non-explicit default marker when registering launcher arguments.
  • Preserves presence-based intent detection for direct programmatic launcher kwargs.
  • Adds coverage for argparse defaults, direct kwargs, aliases, and headless visualizer resolution.
  • Documents the corrected headless-launch behavior.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete blocking or independently actionable non-blocking defects identified.

The new intent marker correctly separates parser defaults from explicit CLI input while retaining direct-kwargs detection, and downstream handling prevents non-explicit defaults from reaching the explicit-visualizer error path.

Important Files Changed

Filename Overview
source/isaaclab/isaaclab/app/app_launcher.py Introduces an argparse intent marker and updates visualizer resolution to distinguish parser defaults from explicit CLI or direct-kwargs selections.
source/isaaclab/test/app/test_kwarg_launch.py Updates alias expectations and adds focused tests for visualizer intent and headless behavior.
source/isaaclab/changelog.d/chaitanya-fix-headless-visualizer-resolution.rst Accurately documents the corrected handling of script-defined visualizer defaults.

Reviews (1): Last reviewed commit: "Fix headless visualizer bug related to p..." | Re-trigger Greptile

@isaaclab-review-bot isaaclab-review-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isaac Lab Review Bot

The explicitness sentinel correctly distinguishes argparse defaults from explicit --viz input and direct kwargs, but it introduces a regression: a script-defined default such as visualizer=["kit"] no longer contributes to headless intent, so an ordinary launch is forced headless when visualizer_intent is absent.

  • Design and architecture: The tri-state explicitness handling is appropriate, but script-defined visualizer defaults are demoted without being transferred to the existing configuration-intent path. Their selected backend remains recorded while headless resolution behaves as though no visualizer was selected.
  • API: Explicit --viz and AppLauncher(visualizer=[...]) retain their existing explicit-selection behavior. However, the argparse integration surface changes materially: parser.set_defaults(visualizer=["kit"]) now causes a normal launch to resolve as headless unless the script separately supplies visualizer_intent.
  • Implementation: Because visualizer_explicit=False bypasses the presence fallback, _resolve_headless_settings reaches its non-explicit branch and consults only _cfg_has_any_visualizers and _cfg_has_kit_visualizer. With no visualizer_intent, both are false, forcing headless even though _cli_visualizer_types contains kit. Non-explicit argparse defaults should feed the configuration-intent state or otherwise preserve their normal-launch semantics.

Minor fixes needed. Posted 1 actionable finding inline.

Automated review; human maintainers own approval decisions.

visualizer_explicit = bool(launcher_args.pop("visualizer_explicit", False))
if not visualizer_explicit and "visualizer" in launcher_args:
visualizer_explicit = raw_visualizers is not None
visualizer_explicit = launcher_args.pop("visualizer_explicit", None)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Warning · Design Architecture — Script default visualizers now silently force headless

With visualizer_explicit=False always present in the parsed namespace, a script using parser.set_defaults(visualizer=["kit"]) takes the else branch of _resolve_headless_settings (lines 938-954). That branch consults only visualizer_intent, which such scripts do not set, so _headless becomes True even on a plain non-headless run, while _cli_visualizer_types still holds kit. Map non-explicit argparse visualizer defaults into _cfg_has_any_visualizers/_cfg_has_kit_visualizer instead of discarding them.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non-explicit argparse visualizer defaults are now properly mapped into _cfg_has_any_visualizers and _cfg_has_kit_visualizer

@ChaitanyaParate
ChaitanyaParate force-pushed the chaitanya/fix-headless-visualizer-resolution branch from 998fdca to 5aaa51b Compare August 31, 2026 05:23
@AntoineRichard

Copy link
Copy Markdown
Collaborator

Thanks for digging into this. I tried to reproduce it before reviewing the code, and I don't think the bug is where the issue says it is.

The first repro command uses --headless, but that flag was removed from the CLI in 3.0. add_app_launcher_args doesn't register it anymore, and there's a test asserting the parser rejects it (test_argparser_launch.py:49). So it never reaches AppLauncher at all — you just get unrecognized arguments: --headless from Hydra.

The second one works fine for me on current develop:

HEADLESS=1 uv run python scripts/environments/zero_agent.py --task Isaac-Cartpole --num_envs 1 --max_steps 5

That exits 0, and the log shows the Kit visualizer being created and running headless. zero_agent goes through simple_agents.py:249, which is exactly the parser.set_defaults(visualizer=["kit"]) case, so it should be hitting the path you describe.

Tracing where the RuntimeError can actually come from: it's raised in _resolve_visualizer_cfgs only when _create_default_visualizer_configs fails to import isaaclab_visualizers.kit. That import is config-only, and isaaclab-visualizers is a hard workspace dependency, so on a normal install it can't fail. Could you paste the full traceback and the output of uv pip list | grep isaaclab? I suspect this is an environment problem rather than a logic one.

On the patch itself, it would cause a regression. Flipping set_defaults to non-explicit doesn't just relax the error check — it makes the whole idiom a no-op downstream. _resolve_visualizer_cfgs branches on cli_explicit, and when that's false it ignores the CLI types entirely and falls back to cfg.visualizer_cfgs, which defaults to []. The _cfg_has_* assignments you added only affect headless resolution inside AppLauncher; they're never published to carb, so SimulationContext doesn't see them.

I applied just the app_launcher.py hunk and reran the command above. The "Registered backend 'kit' for factory Visualizer" line and the KitVisualizer startup warning both disappear — no visualizer gets created. About 30 scripts use this idiom (most of scripts/demos, scripts/tutorials, and the simple agents), so they'd all launch a Kit window with nothing driving it.

One thing in here is worth keeping, though. test_kwarg_launch.py is already failing on develop, and it's the same four tests you touched. Three are newtonnewton_gl alias fallout where the assertions were never updated, and test_matrix_headless_with_viz_names_takes_precedence feeds headless=True into _resolve_visualizer_settings, which stopped honoring it when --headless was deprecated. Those are real and we should fix them, but as a standalone PR — and I'd delete the headless one rather than flip its expectations, since it covers a path that no longer exists.

@matthewtrepte for viz.

@AntoineRichard AntoineRichard left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will not merge unless more information is provided.

@ChaitanyaParate

Copy link
Copy Markdown
Author

I am closing this PR because the root diagnosis was incorrect. As confirmed by a clean-install reproduction, the RuntimeError regarding explicit visualizer intent was not a logic bug in AppLauncher. It was caused by an environment misconfiguration where the isaaclab_visualizers extension failed to load, which masqueraded as a config failure. The proposed fix in this PR would cause a regression by disabling kit visualizer initialization for all non-headless runs that rely on script defaults.

For the full explanation of the root cause, please see the update in #7403.

I have extracted the valid test fixes (the newton_gl alias fallout) into a separate branch and will submit them as a new, standalone PR.

kellyguo11 added a commit that referenced this pull request Sep 4, 2026
… CLI test (#7533)

This PR isolates and fixes the failing tests in `test_kwarg_launch.py`
that surfaced during the investigation of #7403 (and the closed PR
#7444).

When run on a clean `upstream/develop`, `test_kwarg_launch.py` fails on
exactly four tests. This PR addresses them:
1. **Newton Alias Fallout:** Fixed three tests
(`test_parse_visualizer_csv_accepts_comma_delimited_values`,
`test_visualizer_csv_does_not_swallow_hydra_overrides`, and
`test_matrix_cli_kit_newton_with_custom_kit_cfg_intent_non_headless`)
which were failing because they asserted against the deprecated `newton`
alias instead of `newton_gl`. The third test was explicitly renamed to
`test_matrix_cli_kit_newton_gl_with_custom_kit_cfg_intent_non_headless`
to reflect the updated assertion.
2. **Removed Dead CLI Path:** Deleted
`test_matrix_headless_with_viz_names_takes_precedence`. This test was
failing because it passed `headless=True` directly through
`_resolve_visualizer_settings`, simulating the `--headless` CLI flag
which was removed in 3.0 and is no longer supported upstream.
3. **Hygiene Updates (Not Failing):** 
- Replaced `"kit, newton"` with `"kit, newton_gl"` in
`test_parse_visualizer_csv_rejects_spaces_between_entries`. This test
successfully raises an `ArgumentTypeError` before checking aliases so it
wasn't failing, but it was updated to prevent stale strings from
persisting.
- Renamed `test_matrix_no_cli_with_cfg_kit_newton_non_headless` to
`test_matrix_no_cli_with_cfg_kit_newton_gl_non_headless` for naming
consistency. This test also passed previously but carried the stale
alias in its name.

_See the attached `pytest_before_fix.txt` (showing the 4 failures on
develop) and `pytest_after_fix.txt` (showing 47 clean passes) for the
exact run logs._



[pytest_after_fix.txt](https://github.com/user-attachments/files/31785416/pytest_after_fix.txt)

[pytest_before_fix.txt](https://github.com/user-attachments/files/31785426/pytest_before_fix.txt)

## Release backport

- [x] <!-- backport-active-release --> Backport this pull request to the
active release branch after it merges into `develop`

---------

Signed-off-by: Kelly Guo <kellyg@nvidia.com>
Co-authored-by: Kelly Guo <kellyg@nvidia.com>
isaaclab-bot Bot pushed a commit that referenced this pull request Sep 4, 2026
… CLI test (#7533)

This PR isolates and fixes the failing tests in `test_kwarg_launch.py`
that surfaced during the investigation of #7403 (and the closed PR
#7444).

When run on a clean `upstream/develop`, `test_kwarg_launch.py` fails on
exactly four tests. This PR addresses them:
1. **Newton Alias Fallout:** Fixed three tests
(`test_parse_visualizer_csv_accepts_comma_delimited_values`,
`test_visualizer_csv_does_not_swallow_hydra_overrides`, and
`test_matrix_cli_kit_newton_with_custom_kit_cfg_intent_non_headless`)
which were failing because they asserted against the deprecated `newton`
alias instead of `newton_gl`. The third test was explicitly renamed to
`test_matrix_cli_kit_newton_gl_with_custom_kit_cfg_intent_non_headless`
to reflect the updated assertion.
2. **Removed Dead CLI Path:** Deleted
`test_matrix_headless_with_viz_names_takes_precedence`. This test was
failing because it passed `headless=True` directly through
`_resolve_visualizer_settings`, simulating the `--headless` CLI flag
which was removed in 3.0 and is no longer supported upstream.
3. **Hygiene Updates (Not Failing):** 
- Replaced `"kit, newton"` with `"kit, newton_gl"` in
`test_parse_visualizer_csv_rejects_spaces_between_entries`. This test
successfully raises an `ArgumentTypeError` before checking aliases so it
wasn't failing, but it was updated to prevent stale strings from
persisting.
- Renamed `test_matrix_no_cli_with_cfg_kit_newton_non_headless` to
`test_matrix_no_cli_with_cfg_kit_newton_gl_non_headless` for naming
consistency. This test also passed previously but carried the stale
alias in its name.

_See the attached `pytest_before_fix.txt` (showing the 4 failures on
develop) and `pytest_after_fix.txt` (showing 47 clean passes) for the
exact run logs._



[pytest_after_fix.txt](https://github.com/user-attachments/files/31785416/pytest_after_fix.txt)

[pytest_before_fix.txt](https://github.com/user-attachments/files/31785426/pytest_before_fix.txt)

## Release backport

- [x] <!-- backport-active-release --> Backport this pull request to the
active release branch after it merges into `develop`

---------

Signed-off-by: Kelly Guo <kellyg@nvidia.com>
Co-authored-by: Kelly Guo <kellyg@nvidia.com>

(cherry picked from commit cb43dd0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working isaac-lab Related to Isaac Lab team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants