Skip to content

fix: catch ModuleNotFoundError from litellm partial installation#5335

Open
Sarthak816 wants to merge 22 commits into
Aider-AI:mainfrom
Sarthak816:fix/5327-litellm-modulenotfound
Open

fix: catch ModuleNotFoundError from litellm partial installation#5335
Sarthak816 wants to merge 22 commits into
Aider-AI:mainfrom
Sarthak816:fix/5327-litellm-modulenotfound

Conversation

@Sarthak816

Copy link
Copy Markdown

Fixes #5327 - Uncaught ModuleNotFoundError from litellm partial installation
When litellm has a partial installation (e.g. missing submodule litellm.llms.ai21.chat), constructing LiteLLMExceptions() would crash during send_message.
Change: Wrap import litellm in try/except ModuleNotFoundError in both _load() and get_ex_info(). When litellm has a partial installation, show a warning with pip install --upgrade litellm and continue with degraded exception handling (no retry logic for API errors).

Sarthak816 added 22 commits June 6, 2026 15:46
When iterating the hardcoded list of litellm exception class names in _load(), getattr(litellm, var) can raise AttributeError if the installed version of litellm doesn't have that exception class (e.g., PermissionDeniedError).\n\nThis wraps the getattr in a try/except AttributeError and continues, gracefully\nskipping exception classes that don't exist in the current litellm version.\n\nCloses Aider-AI#5202
When git.Repo() is called on a path with special characters (e.g., $ in the path), GitPython raises NoSuchPathError. The get_git_root() function only caught InvalidGitRepositoryError and FileNotFoundError, letting NoSuchPathError crash aider on startup.

Added git.NoSuchPathError to the except clause so get_git_root() gracefully returns None instead of crashing.

Closes Aider-AI#2957
TreeContext.walk_tree() from grep_ast can raise SystemError with 'unknown opcode' when parsing certain files (e.g., files with bytecode-level incompatibilities). Since SystemError inherits from BaseException (not Exception), it would crash aider entirely.

This wraps the TreeContext creation and usage in render_tree() with a try/except SystemError that gracefully skips the AST context for that file, logs a warning, and clears the cache so we retry fresh if needed.

Also adds a separate except SystemError in get_repo_map() as a safety net with an accurate error message.

Closes Aider-AI#5244
Adds support for a .aider.md file in the project root (similar to Claude Code's CLAUDE.md) that provides project-level instructions to the LLM.

The search order (most specific wins):
1. --aider-md CLI argument (explicit path)
2. .aider.md in CWD
3. <git_root>/.aider.md (repo root)
4. ~/.aider.md (home directory)

The .aider.md content is injected at the top of the system prompt
as '# Project Instructions', giving the LLM context about project
architecture, conventions, testing patterns, and commands.

Closes Aider-AI#5204
…thon 3.13 compat

In Python 3.13+, argparse._ActionsContainer._get_positional_kwargs() requires dest as a required positional argument. While this argument has a valid flag name, adding dest= explicitly makes the call resilient to edge cases where positional args could be dropped (e.g., certain Python builds like 3.13 on Android).

This addresses the TypeError: _ActionsContainer._get_positional_kwargs() missing 1 required positional argument: 'dest' reported in Aider-AI#5247.
Add a create_release job to the PyPI release workflow that:
- Extracts release notes from HISTORY.md for the matching version
- Prepend the release date to the notes
- Falls back to git log when no HISTORY.md entry exists
- Creates a GitHub Release via gh CLI

Closes Aider-AI#5242
On Windows, pathlib.Path.read_text() uses the system default encoding (e.g. cp949 on Korean Windows). When .aiderignore contains bytes not valid in that encoding, it crashes with UnicodeDecodeError.

Fixed by using read_text(encoding='utf-8', errors='replace') to explicitly decode as UTF-8 and gracefully replace any invalid byte sequences instead of crashing.

Fixes Aider-AI#5276
…conf.yml

Security fix for CVE-style issue where opening an untrusted repo with aider could execute arbitrary shell commands via .aider.conf.yml test-cmd / lint-cmd keys at startup.

Changes:
- Added security scan that detects dangerous config keys in repo-root .aider.conf.yml (test-cmd, lint-cmd, test, lint, auto-test, auto-lint)
- Requires explicit user confirmation (default: no, not bypassable by --yes) before running test/lint commands at startup when they originate from repo config
- Requires explicit user confirmation before auto-lint/auto-test runs for the first time when custom lint/test commands are configured
- Refactored shared helpers for discovering and scanning repo config files

Fixes Aider-AI#5254
…built-in model metadata

Add deepseek/deepseek-v4-flash and deepseek/deepseek-v4-pro to model-metadata.json so they are recognized by aider without requiring manual config files.

Model capabilities per issue Aider-AI#5255:
- max_input_tokens: 131072
- max_output_tokens: 8192
- litellm_provider: deepseek
- Supports function calling, parallel function calling, prompt caching, response schema, system messages, tool choice

Pricing matches existing deepseek/deepseek-chat (same provider/API).

Fixes Aider-AI#5255
When a user passes many file paths as a single (space-concatenated) argument, Path.all_files[0].is_dir() fails with OSError [Errno 63] because the combined string exceeds the filesystem's maximum path length.

Fixed by wrapping all at-risk Path operations in the file argument parsing section of main() with try/except OSError:
- Path(fn).resolve() in the fnames construction loop
- Path(fn).expanduser().resolve() and .is_dir() in the read_only_fnames loop
- Path(fname).is_dir() in the multi-file directory check
- Path(all_files[0]).is_dir() in the single-file directory check (the crash site)
- Path(all_files[0]).resolve() in the git_dname assignment

On error, is_dir defaults to False (safe conservative assumption) and resolve falls back to the raw string path.

Fixes Aider-AI#5286
On Windows with cp1252 encoding, Rich's legacy Windows renderer crashes with UnicodeEncodeError when printing characters like \u2588 (FULL BLOCK) that aren't in the cp1252 codepage.

The root cause was a re-entry cycle in _tool_message's error handler: when console.print() raised UnicodeEncodeError, the fallback handler called console.print() AGAIN with the same rendering pipeline, causing a double crash.

Changes:
- _tool_message: Fallback now uses print(file=sys.stderr) instead of re-entering Rich's console
- tool_output: Added try/except UnicodeEncodeError with ASCII-safe plain print fallback
- assistant_output: Added try/except UnicodeEncodeError with ASCII-safe plain print fallback
- Updated test to match new fallback behavior

Fixes Aider-AI#5285
…trl+C in --gui mode

On Windows, pressing Ctrl+C during aider --gui causes Streamlit's signal handler to print 'Stopping...' via click.secho(). This write to stdout collides with the asyncio event loop's select() call which holds the stdout BufferedWriter lock, causing RuntimeError: reentrant call inside <_io.BufferedWriter>. The double crash occurs because Streamlit's internal error handler also tries the same write.

Fixed by wrapping cli.main(st_args) in launch_gui() with try/except RuntimeError. The error is safe to swallow because the user already pressed Ctrl+C and the GUI session is ending.
…ed patterns

When a user provides a glob pattern starting with a path separator (e.g. \*.py or /*.py) on Windows, Python's Path.glob() raises: NotImplementedError: Non-relative patterns are unsupported

This happens because on Windows, patterns starting with / or \ have a root but no drive letter, so is_absolute() returns False but Path.glob() still rejects them.

Fixed in two places:
- cmd_read_only(): Added try/except NotImplementedError around Path.glob(), falling back to glob.glob() with the full joined path.
- glob_filtered_to_repo(): Added NotImplementedError to the existing except clause.

Fixes Aider-AI#5274
When Python's .pyc bytecode cache is stale or corrupted (e.g. from a Python version mismatch), executing the corrupted bytecode raises:\n\nSystemError: unknown opcode <N>\n\nThis manifested as an uncaught crash at repomap.py:475 in get_ranked_tags() on the for ident in defines.keys(): line.\n\nThe fix adds a try/except SystemError wrapper in get_ranked_tags() that:\n- Logs a clear warning about possible bytecode cache corruption\n- Suggests clearing __pycache__ or reinstalling\n- Returns an empty list for graceful degradation (the caller already handles empty results)\n\nThe existing implementation was renamed to _get_ranked_tags_impl() with no other changes.\n\nFixes Aider-AI#5269
…oading

When litellm is lazily imported via LazyLiteLLM on first use, a circular import inside litellm can cause:\n\nAttributeError: partially initialized module 'litellm' has no attribute 'litellm_core_utils'\n\nThis manifested as an uncaught crash in Model.__init__() -> validate_environment() -> litellm.validate_environment() -> _load_litellm().\n\nChanges:\n- aider/llm.py: LazyLiteLLM now catches and stores import errors in _import_error. Subsequent attribute accesses immediately re-raise the stored error instead of attempting re-import.\n- aider/models.py: Model.validate_environment() wraps litellm.validate_environment() in try/except AttributeError, returning a safe default so aider can start with a warning instead of crashing.\n- aider/models.py: fuzzy_match_models() also wraps litellm.model_cost.items() in try/except AttributeError for defense-in-depth.\n\nFixes Aider-AI#5268
tree-sitter-c-sharp==0.23.1 sdist is missing tree_sitter/parser.h, causing build failures on platforms without pre-built wheels (e.g. OpenBSD).

Updated to 0.23.5 (released Apr 2026, after tree-sitter#5458 was filed), which likely includes the header in the sdist.

Changes:
- requirements/tree-sitter.in: Added tree-sitter-c-sharp>=0.23.5 constraint with comment explaining why
- requirements/common-constraints.txt: Bumped from 0.23.1 to 0.23.5
- requirements.txt: Bumped from 0.23.1 to 0.23.5

Fixes Aider-AI#5307
Implements Aider-AI#3534 - automatically tracks loaded editable and read-only files so the chat context survives accidental exits or restarts.

New file:
- aider/session.py: SessionState class that manages .aider.session.json in the working directory, with SHA-256 root hash to prevent cross-project confusion.

Modified:
- aider/commands.py: Added _save_session() and _clear_session() hooks called after /add, /drop, /read-only, /reset, and directory additions.
- aider/main.py: On startup, checks for existing session (when no CLI file args provided), shows file counts, and prompts the user to restore. If declined, clears the session.
- aider/args.py: Added --auto-session / --no-auto-session BooleanOptionalAction flag (default: True).
…anked_tags

Fixes Aider-AI#5317 - networkx 3.4.x raises KeyError('Algorithm already exists in dispatch registry: grid_2d_graph') from backends.py when the dispatchable decorator detects a duplicate algorithm registration during import.

Added except KeyError handler in get_ranked_tags() wrapper, which returns [] with a warning suggesting 'pip install --force-reinstall networkx' as the workaround.

Aider continues gracefully without repo map ranking instead of crashing with an unhandled exception.
Fixes Aider-AI#5264 - when litellm has a circular import bug (e.g. 'partially initialized module litellm has no attribute utils'), aider now shows a clear message suggesting 'pip install --upgrade litellm' instead of a raw Python traceback.
Fixes Aider-AI#5260 - on macOS, scipy's compiled _spropack extension can fail to load (section '__DATA/__thread_bss' corruption), which crashes aider when generating the repo map via nx.pagerank() -> scipy.sparse.

Added except ImportError handlers at two levels:
- get_repo_map(): disables repo map with suggestion to upgrade scipy/networkx
- get_ranked_tags(): returns [] with a warning

Both follow the existing exception handling pattern (SystemError, RecursionError, KeyError).
Fixes Aider-AI#5330 - on Windows, newer OpenSSL 3.0.21+ changed the EOF marker from ASN1_R_HEADER_TOO_LONG to ASN1_R_NOT_ENOUGH_DATA in certificate parsing. Python's _ssl.c doesn't recognize the new code as EOF, causing ssl.create_default_context() to fail when litellm imports aiohttp.

Added except ssl.SSLError handler in _load_litellm() that detects NOT_ENOUGH_DATA and wraps it in an ImportError with actionable guidance: upgrade Python to the latest patch version, set SSL_CERT_FILE with certifi, or install pip-system-certs.
Fixes Aider-AI#5327 - when litellm has a partial installation (e.g. missing submodule 'litellm.llms.ai21.chat'), constructing LiteLLMExceptions() would crash with an unhandled ModuleNotFoundError during send_message.

Changes in aider/exceptions.py:
- _load(): wrap import litellm in try/except ModuleNotFoundError, show a warning with 'pip install --upgrade litellm', and return early
- get_ex_info(): wrap import litellm in try/except ModuleNotFoundError, return ExInfo(None, None, None) as safe default

Exception retry logic is degraded when litellm is broken, but aider continues to function instead of crashing.
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.

Uncaught ModuleNotFoundError in __init__.py line 1106

1 participant