Skip to content

Modernize Hydra’s default exception rendering #3361

Description

@omry

Problem

Hydra’s default exception rendering currently offers two extremes:

  1. CompactHydraException subclasses are reduced to their message.
  2. HYDRA_FULL_ERROR=1 exposes the complete Python traceback, including Hydra and OmegaConf implementation frames.

run_and_report() currently handles compact exceptions with:

if isinstance(ex, CompactHydraException):
    sys.stderr.write(str(ex) + os.linesep)
    if isinstance(ex.__cause__, OmegaConfBaseException):
        sys.stderr.write(str(ex.__cause__) + os.linesep)

This loses useful information:

  • The user-code location that triggered the error.
  • Explicit chained causes other than OmegaConfBaseException.
  • The distinction between the high-level Hydra error and its underlying cause.

This is especially problematic for callable APIs such as hydra.utils.instantiate(). An application may receive:

Error locating target my_app.Model
full_key: model._target_

without seeing either the line of application code that called instantiate() or the chained ImportError.

The current behavior conflates two separate goals:

  • Hide irrelevant Hydra and OmegaConf implementation frames.
  • Hide all traceback frames.

Hydra should do the first without doing the second.

Goal

Modernize Hydra’s default exception rendering so that ordinary users receive a concise but useful traceback containing:

  • Relevant application frames.
  • The high-level Hydra exception and configuration context.
  • Explicit chained causes in a clear, controlled form.
  • No routine Hydra or OmegaConf implementation frames.

For example:

Traceback (most recent call last):
  File "/app/train.py", line 42, in build_model
    model = instantiate(cfg.model)

InstantiationException: Error locating target my_app.Model
full_key: model._target_

Caused by: ImportError: No module named my_app

The exact formatting can be decided during implementation, but the information model should be consistent.

Direction

Replace the current message-only handling of CompactHydraException with a shared exception-rendering policy.

The default renderer should:

  • Preserve user-code frames, including:
    • The task-function frame.
    • Calls to public Hydra APIs such as instantiate().
    • User target or constructor frames when those raise.
  • Filter routine internal frames from hydra.* and omegaconf.*.
  • Render explicit __cause__ chains without requiring each wrapper exception to copy the cause into its own message.
  • Render each cause once.
  • Preserve useful Hydra metadata such as full_key.
  • Avoid exposing complete internal tracebacks merely to explain a common application error.
  • Fall back safely to standard Python traceback rendering if Hydra cannot apply its filtering policy.

HYDRA_FULL_ERROR=1 and debugger detection should continue to bypass filtering and expose the complete native Python traceback.

Acceptance criteria

  • An InstantiationException raised inside a Hydra task displays the application line that called instantiate().
  • If target construction enters user code and that code raises, the relevant user target frames remain visible.
  • Hydra and OmegaConf implementation frames are omitted from default output.
  • An explicit chained ImportError is displayed as a cause without being copied into the InstantiationException message.
  • Chained causes appear exactly once.
  • Compact configuration errors retain their existing actionable messages and configuration metadata.
  • HYDRA_FULL_ERROR=1 continues to produce the complete native traceback and chain.
  • Running under a debugger continues to bypass sanitization.
  • Focused tests cover:
    • Compact exceptions with and without causes.
    • instantiate() call-site failures.
    • Exceptions raised inside user targets.
    • OmegaConf causes.
    • Full-error and debugger behavior.
  • Current user and development documentation explains the default filtered traceback and the full-error debugging facility.

Related work

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions