Skip to content

fix: print install hint when no suitable ImageWriter found#8761

Open
yashnaiduu wants to merge 5 commits intoProject-MONAI:devfrom
yashnaiduu:fix/helpful-writer-missing-error-msg
Open

fix: print install hint when no suitable ImageWriter found#8761
yashnaiduu wants to merge 5 commits intoProject-MONAI:devfrom
yashnaiduu:fix/helpful-writer-missing-error-msg

Conversation

@yashnaiduu
Copy link

Fixes #7980

What changed

When resolve_writer() fails to find a backend for a given file extension,
the error was generic and unhelpful:

OptionalImportError: No ImageWriter backend found for nii.gz.

Users had no idea which package was missing.

Fix

Added a format-to-package lookup in resolve_writer() that appends
an install hint for common formats:

Format Install
.nii, .nii.gz pip install nibabel
.mha, .mhd, .nrrd pip install itk
.png, .jpg, .tif, .bmp pip install pillow

After this fix

OptionalImportError: No ImageWriter backend found for 'nii.gz'.
Try installing the required package: pip install nibabel

No behaviour change for users who already have the packages installed.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 1, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a new public mapping FILETYPE_HINT: dict[str, str] in monai/data/image_writer.py mapping common extensions to packages (e.g., "nii""nibabel", "png""pillow", "mha""itk"). resolve_writer now, when no writer backends are found and error_if_not_found is True, raises an OptionalImportError whose message quotes the requested extension and, if a hint exists in FILETYPE_HINT, appends a "Try: pip install " suggestion. No other public signatures or writer selection behavior were changed.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive Description covers the problem, solution, and examples well. However, it omits all checkboxes from the template, particularly missing indicators for breaking changes, tests, and documentation updates. Complete the 'Types of changes' section with appropriate checkboxes indicating whether tests were added, existing functionality breaks, or documentation was updated.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed Title clearly and concisely describes the main change: adding helpful install hints to error messages when ImageWriter backends are missing.
Linked Issues check ✅ Passed Changes fully satisfy issue #7980 requirements: added FILETYPE_HINT mapping for common formats and augmented error messages with installation hints as requested.
Out of Scope Changes check ✅ Passed All changes directly support the core objective of issue #7980. The new FILETYPE_HINT constant and enhanced error messages are narrowly focused on the stated feature.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
monai/data/image_writer.py (1)

120-132: Consider moving _INSTALL_HINTS to module level.

Defining the dict inside the conditional recreates it on each error. Moving it to module scope improves clarity and avoids repeated allocation (albeit minor since it's only on the error path).

Proposed refactor

At module level (e.g., after line 64):

# Install hints for common image formats
_INSTALL_HINTS: dict[str, str] = {
    "nii": "nibabel",
    "nii.gz": "nibabel",
    "mha": "itk",
    "mhd": "itk",
    "nrrd": "itk",
    "png": "pillow",
    "jpg": "pillow",
    "jpeg": "pillow",
    "tif": "pillow",
    "tiff": "pillow",
    "bmp": "pillow",
}

Then in the function:

     if not avail_writers and error_if_not_found:
-        # install hints — user ko guess nahi karna padega
-        _INSTALL_HINTS: dict = {
-            "nii": "nibabel",
-            "nii.gz": "nibabel",
-            "mha": "itk",
-            "mhd": "itk",
-            "nrrd": "itk",
-            "png": "pillow",
-            "jpg": "pillow",
-            "jpeg": "pillow",
-            "tif": "pillow",
-            "tiff": "pillow",
-            "bmp": "pillow",
-        }
         hint = _INSTALL_HINTS.get(fmt)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@monai/data/image_writer.py` around lines 120 - 132, The _INSTALL_HINTS dict
is currently created inside the error-handling branch causing repeated
recreation; move the _INSTALL_HINTS mapping to module scope (as a constant) so
it is allocated once and can be reused, update its type annotation to dict[str,
str] (or Mapping[str, str]) and remove the inline definition from the
conditional, then import/reference the module-level _INSTALL_HINTS from the code
that previously built it (e.g., the image-writing function/exception-handling
block) so the error message assembly uses the shared constant.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@monai/data/image_writer.py`:
- Line 119: Replace the Hindi inline comment "# install hints — user ko guess
nahi karna padega" with an English equivalent for clarity and maintainability
(for example: "# installation hints — so the user doesn't have to guess").
Locate this comment in monai/data/image_writer.py (search for the exact Hindi
text) and update it to the English phrasing while preserving surrounding code
and punctuation.

---

Nitpick comments:
In `@monai/data/image_writer.py`:
- Around line 120-132: The _INSTALL_HINTS dict is currently created inside the
error-handling branch causing repeated recreation; move the _INSTALL_HINTS
mapping to module scope (as a constant) so it is allocated once and can be
reused, update its type annotation to dict[str, str] (or Mapping[str, str]) and
remove the inline definition from the conditional, then import/reference the
module-level _INSTALL_HINTS from the code that previously built it (e.g., the
image-writing function/exception-handling block) so the error message assembly
uses the shared constant.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 1e3d29b and e65e0f8.

📒 Files selected for processing (1)
  • monai/data/image_writer.py

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
monai/data/image_writer.py (1)

120-133: Move install-hint mapping to module scope.

_INSTALL_HINTS is recreated on every miss. Making it a module-level constant improves maintainability and avoids repeated allocation.

Proposed refactor
+INSTALL_HINTS: Mapping[str, str] = {
+    "nii": "nibabel",
+    "nii.gz": "nibabel",
+    "mha": "itk",
+    "mhd": "itk",
+    "nrrd": "itk",
+    "png": "pillow",
+    "jpg": "pillow",
+    "jpeg": "pillow",
+    "tif": "pillow",
+    "tiff": "pillow",
+    "bmp": "pillow",
+}
+
 def resolve_writer(ext_name, error_if_not_found=True) -> Sequence:
@@
     if not avail_writers and error_if_not_found:
-        _INSTALL_HINTS: dict = {
-            "nii": "nibabel",
-            "nii.gz": "nibabel",
-            "mha": "itk",
-            "mhd": "itk",
-            "nrrd": "itk",
-            "png": "pillow",
-            "jpg": "pillow",
-            "jpeg": "pillow",
-            "tif": "pillow",
-            "tiff": "pillow",
-            "bmp": "pillow",
-        }
-        hint = _INSTALL_HINTS.get(fmt)
+        hint = INSTALL_HINTS.get(fmt)
         extra = f" Try installing the required package: pip install {hint}" if hint else ""
         raise OptionalImportError(f"No ImageWriter backend found for '{fmt}'.{extra}")

As per coding guidelines, "Suggest any enhancements for code improving efficiency, maintainability, comprehensibility, and correctness."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@monai/data/image_writer.py` around lines 120 - 133, The _INSTALL_HINTS dict
is being recreated each time the code path runs; move it to module scope as a
constant (e.g., INSTALL_HINTS) and change the lookup in the function to use
INSTALL_HINTS.get(fmt) (keep the variable name hint and the fmt parameter as-is)
so the mapping is allocated once and the function simply performs a lookup,
improving maintainability and avoiding repeated allocations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@monai/data/image_writer.py`:
- Around line 120-133: The _INSTALL_HINTS dict is being recreated each time the
code path runs; move it to module scope as a constant (e.g., INSTALL_HINTS) and
change the lookup in the function to use INSTALL_HINTS.get(fmt) (keep the
variable name hint and the fmt parameter as-is) so the mapping is allocated once
and the function simply performs a lookup, improving maintainability and
avoiding repeated allocations.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between e65e0f8 and 1191cbc.

📒 Files selected for processing (1)
  • monai/data/image_writer.py

When resolve_writer() fails to find a backend for a given file extension,
the error was generic. Added a format-to-package lookup that appends
an install hint (e.g. pip install nibabel) for common formats.

Fixes Project-MONAI#7980

Signed-off-by: yashnaiduu <yashnaiduu@users.noreply.github.com>
@yashnaiduu yashnaiduu force-pushed the fix/helpful-writer-missing-error-msg branch from 77273f4 to b809c77 Compare March 1, 2026 18:36
Signed-off-by: yashnaiduu <yashnaiduu@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
monai/data/image_writer.py (1)

119-132: Avoid extension list drift between init() and resolve_writer().

The extension-to-format knowledge is duplicated in two places. Please centralize it in one module-level mapping/constant and reuse it in both paths.

Proposed refactor
+EXT_TO_INSTALL_HINT: dict[str, str] = {
+    "nii": "nibabel",
+    "nii.gz": "nibabel",
+    "mha": "itk",
+    "mhd": "itk",
+    "nrrd": "itk",
+    "png": "pillow",
+    "jpg": "pillow",
+    "jpeg": "pillow",
+    "tif": "pillow",
+    "tiff": "pillow",
+    "bmp": "pillow",
+}
...
-        install_hints: dict = {
-            "nii": "nibabel",
-            "nii.gz": "nibabel",
-            "mha": "itk",
-            "mhd": "itk",
-            "nrrd": "itk",
-            "png": "pillow",
-            "jpg": "pillow",
-            "jpeg": "pillow",
-            "tif": "pillow",
-            "tiff": "pillow",
-            "bmp": "pillow",
-        }
-        hint = install_hints.get(fmt)
+        hint = EXT_TO_INSTALL_HINT.get(fmt)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@monai/data/image_writer.py` around lines 119 - 132, The
extension-to-format/package mapping is duplicated between init() and
resolve_writer(); extract the dict into a single module-level constant (e.g.
EXTENSION_INSTALL_HINTS or EXTENSION_MAP) and have both init() and
resolve_writer() reference that constant instead of defining their own mapping;
update any variable names used in init() and resolve_writer() to use the shared
symbol and remove the local duplicate dict so both paths always use the same
source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@monai/data/image_writer.py`:
- Around line 119-132: The extension-to-format/package mapping is duplicated
between init() and resolve_writer(); extract the dict into a single module-level
constant (e.g. EXTENSION_INSTALL_HINTS or EXTENSION_MAP) and have both init()
and resolve_writer() reference that constant instead of defining their own
mapping; update any variable names used in init() and resolve_writer() to use
the shared symbol and remove the local duplicate dict so both paths always use
the same source of truth.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 1191cbc and b809c77.

📒 Files selected for processing (1)
  • monai/data/image_writer.py

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
monai/data/image_writer.py (1)

119-131: Move install_hints to a module-level constant.

Rebuilding this dict on every not-found path is avoidable. Defining it once near EXT_WILDCARD will improve maintainability and keep resolve_writer focused.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@monai/data/image_writer.py` around lines 119 - 131, Move the local
install_hints dict out of resolve_writer and declare it as a module-level
constant (e.g., INSTALL_HINTS) near EXT_WILDCARD so it's constructed once at
import time; then update resolve_writer to reference that constant instead of
rebuilding the dict, keeping function logic unchanged and only replacing the
local variable name with INSTALL_HINTS.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@monai/data/image_writer.py`:
- Around line 119-134: Add unit tests for the new install-hint behavior in
monai.data.image_writer: exercise the mapping stored in install_hints by calling
the ImageWriter error path (the code that raises OptionalImportError) with
formats "nii.gz", "png", "nrrd" and an unknown extension (e.g., "abc"); for each
of the first three assert the raised OptionalImportError message contains the
expected package string ("nibabel", "pillow", "itk" respectively) and for the
unknown extension assert the message does not contain a "pip install" hint;
ensure tests reference the image_writer module and OptionalImportError so they
fail if the mapping or message changes.

---

Nitpick comments:
In `@monai/data/image_writer.py`:
- Around line 119-131: Move the local install_hints dict out of resolve_writer
and declare it as a module-level constant (e.g., INSTALL_HINTS) near
EXT_WILDCARD so it's constructed once at import time; then update resolve_writer
to reference that constant instead of rebuilding the dict, keeping function
logic unchanged and only replacing the local variable name with INSTALL_HINTS.

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Cache: Disabled due to data retention organization setting

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between b809c77 and 6a5238f.

📒 Files selected for processing (1)
  • monai/data/image_writer.py

Comment on lines +119 to +134
install_hints: dict = {
"nii": "nibabel",
"nii.gz": "nibabel",
"mha": "itk",
"mhd": "itk",
"nrrd": "itk",
"png": "pillow",
"jpg": "pillow",
"jpeg": "pillow",
"tif": "pillow",
"tiff": "pillow",
"bmp": "pillow",
}
hint = install_hints.get(fmt)
extra = f" Try installing the required package: pip install {hint}" if hint else ""
raise OptionalImportError(f"No ImageWriter backend found for '{fmt}'.{extra}")
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add regression tests for the new install-hint behavior.

Line 119 through Line 134 changes user-facing error output and format mapping, but no tests are included here. Please add cases for nii.gz -> nibabel, png -> pillow, nrrd -> itk, and unknown extensions (no hint).

As per coding guidelines, "Ensure new or modified definitions will be covered by existing or new unit tests."

🧰 Tools
🪛 Ruff (0.15.2)

[warning] 134-134: Avoid specifying long messages outside the exception class

(TRY003)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@monai/data/image_writer.py` around lines 119 - 134, Add unit tests for the
new install-hint behavior in monai.data.image_writer: exercise the mapping
stored in install_hints by calling the ImageWriter error path (the code that
raises OptionalImportError) with formats "nii.gz", "png", "nrrd" and an unknown
extension (e.g., "abc"); for each of the first three assert the raised
OptionalImportError message contains the expected package string ("nibabel",
"pillow", "itk" respectively) and for the unknown extension assert the message
does not contain a "pip install" hint; ensure tests reference the image_writer
module and OptionalImportError so they fail if the mapping or message changes.

Copy link
Member

@ericspod ericspod left a comment

Choose a reason for hiding this comment

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

Hi @yashnaiduu thanks for this update. I think it's fine as is with my suggested comment, but we should add at least one test to ensure the behaviour works. You would need only a test to check that the text in the exception message shows up to suggest a package when not installed, and doesn't when the file extension isn't known. You may need to mock the dictionary to emulate a package being missing (which you can do if it's a global variable).

avail_writers.append(_writer)
if not avail_writers and error_if_not_found:
raise OptionalImportError(f"No ImageWriter backend found for {fmt}.")
install_hints: dict = {
Copy link
Member

Choose a reason for hiding this comment

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

I would put this dictionary into a global variable at the top of this file. A name like FILETYPE_HINT or something else sticking to the all-caps convention would work. This keeps the dictionary easily found and modifiable at runtime.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
monai/data/image_writer.py (2)

134-136: Document the new OptionalImportError path.

resolve_writer() now has format-specific error text, but its docstring still does not describe the raised exception. Please add a Raises: section for this branch.

As per coding guidelines, "Docstrings should be present for all definition which describe each variable, return value, and raised exception in the appropriate section of the Google-style of docstrings."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@monai/data/image_writer.py` around lines 134 - 136, Update the
resolve_writer() docstring to include a "Raises:" section that documents
OptionalImportError being raised when no ImageWriter backend is available for
the requested fmt; mention that the exception message includes format-specific
guidance (uses FILETYPE_HINT to suggest a pip install) so callers know to expect
and handle OptionalImportError when a backend is missing.

66-79: Clarify whether FILETYPE_HINT is public.

This new all-caps module constant is omitted from __all__, so its visibility is unclear. Either export it there or rename it _FILETYPE_HINT to keep the API surface explicit.

As per coding guidelines, "Suggest any enhancements for code improving efficiency, maintainability, comprehensibility, and correctness."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@monai/data/image_writer.py` around lines 66 - 79, The module-level constant
FILETYPE_HINT is defined in all-caps but not exported via __all__, making its
intended public visibility ambiguous; either add "FILETYPE_HINT" to the module's
__all__ list to explicitly export it, or rename it to _FILETYPE_HINT to mark it
private and update any internal references (e.g., usages in ImageWriter
backends) accordingly so imports and tests remain correct; choose one approach
and apply it consistently across the module (update __all__ or rename symbol and
references).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@monai/data/image_writer.py`:
- Around line 134-136: Update the resolve_writer() docstring to include a
"Raises:" section that documents OptionalImportError being raised when no
ImageWriter backend is available for the requested fmt; mention that the
exception message includes format-specific guidance (uses FILETYPE_HINT to
suggest a pip install) so callers know to expect and handle OptionalImportError
when a backend is missing.
- Around line 66-79: The module-level constant FILETYPE_HINT is defined in
all-caps but not exported via __all__, making its intended public visibility
ambiguous; either add "FILETYPE_HINT" to the module's __all__ list to explicitly
export it, or rename it to _FILETYPE_HINT to mark it private and update any
internal references (e.g., usages in ImageWriter backends) accordingly so
imports and tests remain correct; choose one approach and apply it consistently
across the module (update __all__ or rename symbol and references).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e71e42b3-e955-447c-afd5-6d98678816ca

📥 Commits

Reviewing files that changed from the base of the PR and between 6a5238f and 639f12e.

📒 Files selected for processing (1)
  • monai/data/image_writer.py

@yashnaiduu
Copy link
Author

Thanks for the review @ericspod! I've moved the dictionary to a module-level constant FILETYPE_HINT as suggested and also added the requested unit tests in tests/data/test_image_rw.py.

@yashnaiduu yashnaiduu force-pushed the fix/helpful-writer-missing-error-msg branch from 3a656a8 to 3f2a217 Compare March 7, 2026 15:06
yashnaiduu and others added 3 commits March 7, 2026 20:36
…l hints to module-level FILETYPE_HINT constant

Signed-off-by: YASH NAIDU <152394598+yashnaiduu@users.noreply.github.com>
Signed-off-by: yashnaiduu <yashnaidu@example.com>
… FILETYPE_HINT and resolve_writer error message

Added tests for file type hints and error handling in image writer.

Signed-off-by: YASH NAIDU <152394598+yashnaiduu@users.noreply.github.com>
Signed-off-by: yashnaiduu <yashnaidu@example.com>
Signed-off-by: yashnaiduu <yashnaidu@example.com>
@yashnaiduu yashnaiduu force-pushed the fix/helpful-writer-missing-error-msg branch from 3f2a217 to 5fc5be5 Compare March 7, 2026 15:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Print what package should be installed when suitable writer is missing

2 participants