Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Bash & Python Automation By James Joyner IV · · 9 min read Last reviewed Jul 2026

Python Error Guide: 'ImportError: cannot import name' — Cause, Fix, and Troubleshooting Guide

Quick answer

Fix Python 'ImportError: cannot import name': resolve circular imports, wrong names, version mismatches, and files that shadow the real module.

  • #python
  • #automation
  • #troubleshooting
  • #errors
Free toolkit

Stuck on this Bash & Python Automation error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Overview

Python raises this when a from X import Y finds the module X but not the name Y in it:

ImportError: cannot import name 'get_client' from 'app.aws' (/srv/app/aws.py)

Unlike ModuleNotFoundError (the module itself is missing), this means the module imported fine but the specific attribute you asked for wasn’t defined at the moment the import ran. The parenthetical path tells you exactly which file Python loaded — often the real clue, because it may not be the file you think. The two dominant causes are a circular import (the name isn’t defined yet because the module is mid-load) and a name that genuinely doesn’t exist (typo, renamed in a new version, or a file shadowing the package).

Symptoms

  • The message names both the symbol and the module, with the loaded file path in parentheses.
  • The import works from one entry point but fails from another (a hallmark of circular imports).
  • It started after upgrading a dependency (the symbol was removed or renamed).
  • python3 -c "import app.aws; print(app.aws.__file__)" shows a surprising path — a local file shadowing the intended package.

Common Root Causes

  • Circular import — module A imports B at top level, B imports A; whichever loads second sees the other only half-initialized, so the name isn’t defined yet.
  • Typo or wrong name — the symbol is spelled differently, or lives in a different submodule.
  • Renamed/removed in a new version — a library moved or deleted the symbol between releases.
  • A local file shadows the real module — a queue.py, email.py, or aws.py next to your script is imported instead of the stdlib/third-party one.
  • The name is defined below the import in the target module, or conditionally (inside an if/try) and that branch didn’t run.
  • Importing from __init__.py before the symbol is re-exported.

Diagnostic Workflow

Confirm which file is actually being loaded — this catches shadowing instantly:

python3 -c "import app.aws as m; print(m.__file__)"

List what the module really exports:

python3 -c "import app.aws as m; print([n for n in dir(m) if not n.startswith('_')])"

Detect a circular import from the traceback — it shows the import chain looping back to a module still being initialized. Reproduce with -v to see load order:

python3 -v -c "import app.main" 2>&1 | grep -i 'import app'

Check a library’s version if a symbol vanished:

python3 -c "import somelib; print(somelib.__version__)"

Example Root Cause Analysis

A worker failed to start:

ImportError: cannot import name 'notify' from partially initialized module 'app.tasks'
(most likely due to a circular import) (/srv/app/tasks.py)

Python 3 helpfully flagged partially initialized module ... most likely due to a circular import. The two files:

# app/tasks.py
from app.notify import send      # top of file
def notify(msg): send(msg)

# app/notify.py
from app.tasks import notify      # top of file  <-- circular
def send(msg): ...

tasks imported notify, which imported tasks right back before notify was defined. The clean fix is to break the cycle — move the import inside the function so it resolves lazily, after both modules finish loading:

# app/notify.py
def send(msg):
    from app.tasks import notify   # deferred import breaks the cycle
    ...

Better still, factor the shared piece into a third module both import, so neither depends on the other at load time.

Prevention Best Practices

  • Break circular imports by moving the import inside the function/method that uses it, or extracting shared code into a lower-level module.
  • Verify module.__file__ whenever an import is mysterious — it exposes local files shadowing real packages.
  • Never name a file after a stdlib/third-party module (queue.py, email.py, logging.py) in your package or script directory.
  • Pin dependency versions and read changelogs; a removed/renamed symbol is a version problem, not your code.
  • Keep imports at module top where possible, but use deferred imports deliberately to resolve cycles.
  • Design layered modules — higher layers import lower layers, never the reverse, so cycles can’t form.

Quick Command Reference

python3 -c "import pkg.mod as m; print(m.__file__)"   # which file loaded?
python3 -c "import pkg.mod as m; print(dir(m))"        # what does it export?
python3 -v -c "import pkg.main" 2>&1 | grep import     # see load order
python3 -c "import lib; print(lib.__version__)"        # symbol removed?
grep -rn "import notify\|from app.tasks" app/          # map the import graph

Conclusion

ImportError: cannot import name means the module loaded but the requested symbol wasn’t there when the import ran. The parenthetical file path is your first clue: check it for a local file shadowing a real module, then check for a circular import (Python 3 often says partially initialized module), a typo, or a symbol removed in a new library version. Break cycles with deferred imports or a shared lower-level module, and never shadow a stdlib name with your own file.

Free download · 368-page PDF

Fixed it? Get 500 Bash & Python Automation & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.