What's Actually Happening
You're trying to add a new dependency or update existing packages in your Poetry-managed Python project, but the lock file resolution fails with version conflict errors. Multiple packages require different versions of the same dependency, and Poetry cannot find a compatible resolution.
The Error You'll See
Poetry lock fails with conflicts:
```bash $ poetry add requests>=2.28.0
Updating dependencies Resolving dependencies...
SolverProblemError
Because no versions of requests match >=2.28.0,<3.0.0 and requests (2.28.0) depends on urllib3 (>=1.21.1,<1.27), requests (>=2.28.0) requires urllib3 (>=1.21.1,<1.27).
Because myproject depends on urllib3 (>=2.0.0) and requests (>=2.28.0) requires urllib3 (>=1.21.1,<1.27), version solving failed. ```
Lock resolution timeout:
```bash $ poetry lock
Resolving dependencies... ConnectionError
Could not resolve dependencies. Resolution took too long.
Potential causes: - The dependency graph is too complex - Version constraints are too restrictive - Network connectivity issues ```
Hash mismatch error:
```bash $ poetry install
RuntimeError
The lock file is not up to date with the pyproject.toml.
Please run poetry lock to update the lock file.
Hash mismatch: Expected: abc123... Actual: def456... ```
Why This Happens
- 1.Version constraint conflicts - Multiple packages require incompatible versions
- 2.Circular dependencies - Package A depends on B which depends on A
- 3.Overly strict constraints - Exact version pins create conflicts
- 4.Outdated lock file - pyproject.toml changes not synced to lock
- 5.PyPI package conflicts - Different packages claiming same namespace
- 6.Platform-specific deps - Wheels not available for your platform
- 7.Private package conflicts - Custom packages with wrong metadata
- 8.Dependency removed - Package no longer exists on PyPI
Step 1: Understand Current Dependency State
```bash # Check current dependencies: poetry show
# Show dependency tree: poetry show --tree
# Example output: # myproject 1.0.0 # ├── requests 2.28.0 # │ ├── urllib3 >=1.21.1,<1.27 # │ ├── certifi >=2017.4.17 # │ └── charset-normalizer >=2.0,<3.0 # └── urllib3 2.0.0 (conflict!)
# Check specific package: poetry show requests
# Show all versions of a package: poetry search urllib3
# Check why a package is installed: poetry show urllib3 --why
# View pyproject.toml: cat pyproject.toml
# Example: [tool.poetry.dependencies] python = "^3.10" requests = ">=2.28.0" urllib3 = ">=2.0.0" # Conflicts with requests!
# View lock file status: poetry lock --check
# Check for outdated packages: poetry show --outdated ```
Step 2: Identify the Conflict Source
```bash # Run verbose lock to see resolution process: poetry lock -vvv
# Output shows each step: # • Checking requests (2.28.0) # • Requires urllib3 >=1.21.1,<1.27 # • Conflicts with urllib3 2.0.0 in pyproject.toml # • Trying requests 2.31.0... # • Requires urllib3 >=1.26.0,<2.1.0 # • Compatible with urllib3 2.0.0!
# Use debug mode: poetry lock --debug
# Check specific constraint: poetry show urllib3 # Output: # name : urllib3 # version : 2.0.0 # description : HTTP library with thread-safe... # required : >=2.0.0 (from pyproject.toml) # dependencies : # - requests requires <1.27 (conflict!)
# Find which package requires conflicting version: pip show requests | grep Requires # Requires: urllib3>=1.21.1,<1.27
# Check package metadata on PyPI: curl -s https://pypi.org/pypi/requests/json | jq '.info.requires_dist'
# Check dependency requirements in lock file: grep -A 20 'name = "requests"' poetry.lock ```
Step 3: Relax Version Constraints
```bash # Edit pyproject.toml to relax constraints: # Before: [tool.poetry.dependencies] python = "^3.10" requests = "==2.28.0" # Too strict! urllib3 = "==2.0.0" # Too strict!
# After (use caret or tilde): [tool.poetry.dependencies] python = "^3.10" requests = "^2.28" # Allows 2.28.x, 2.29.x, etc. urllib3 = "^2.0" # Allows 2.0.x, 2.1.x, etc.
# Or use range: requests = ">=2.28,<3.0" urllib3 = ">=1.26,<3.0" # Compatible with requests
# For removing urllib3 (let requests manage it): [tool.poetry.dependencies] python = "^3.10" requests = "^2.28" # Remove urllib3 line - requests will bring compatible version
# Update lock after editing: poetry lock
# Or use add with version update: poetry add requests@^2.31 --allow-prereleases
# For specific compatible versions: poetry add requests==2.31.0 urllib3==2.0.0 # Check if they're compatible first ```
Step 4: Use Poetry's Conflict Resolution Commands
```bash # Clear cache before resolving: poetry cache clear pypi --all
# Try lock with different strategies: poetry lock --no-update # Don't update existing packages poetry lock --no-cache # Bypass cache entirely
# For persistent conflicts, use: poetry lock --no-update --no-cache
# Regenerate lock file completely: rm poetry.lock poetry lock
# Or use Python's built-in solver: poetry config solver.use-poetry-solver false poetry lock
# Try legacy installer: poetry config installer.modern-installation false poetry install
# Use experimental parallel install: poetry config installer.max-workers 4 poetry install
# Check configuration: poetry config --list ```
Step 5: Handle Circular Dependencies
```bash # Identify circular dependencies: poetry show --tree | grep -E "(^\S|├──|└──)" | sort | uniq -d
# If circular found, break the cycle: # Example: package-a -> package-b -> package-a
# Option 1: Remove one dependency: poetry remove package-b
# Option 2: Use extras to make optional: [tool.poetry.dependencies] package-a = {version = "^1.0", extras = ["full"]} package-b = {version = "^1.0", optional = true}
[tool.poetry.extras] full = ["package-b"]
# Option 3: Use dev dependencies: [tool.poetry.group.dev.dependencies] package-b = "^1.0"
# Verify tree after changes: poetry show --tree
# Lock after resolving circular: poetry lock
# Check for remaining cycles: poetry show --tree 2>&1 | head -50 ```
Step 6: Update Specific Packages to Resolve Conflict
```bash # Update requests to version that supports urllib3 2.0: poetry add requests@^2.31.0
# requests 2.31.0 requires urllib3 >=1.26.0,<3.0.0 # Compatible with urllib3 2.0.0!
# Or update all packages: poetry update
# Update specific package: poetry update requests
# Update with latest version: poetry add requests@latest
# Pin to compatible versions manually: # 1. Check compatibility matrix: pip index versions requests pip index versions urllib3
# 2. Find compatible pair: # requests 2.31.0 works with urllib3 1.26.0-2.1.0 # requests 2.32.0 works with urllib3 2.0+
# 3. Add compatible versions: poetry add requests==2.32.0 urllib3==2.0.0
# Verify resolution: poetry show requests urllib3 ```
Step 7: Handle Platform-Specific Dependencies
```bash # Check platform markers in lock file: grep -A 5 'markers = ' poetry.lock
# Example platform-specific deps: [tool.poetry.dependencies] pywin32 = {version = "^300", markers = "sys_platform == 'win32'"} pyobjc = {version = "^9.0", markers = "sys_platform == 'darwin'"}
# For wheels not available: poetry config installer.no-binary :all: poetry install
# Or for specific package: poetry config installer.no-binary numpy poetry add numpy
# Check available wheels: curl -s https://pypi.org/pypi/numpy/json | jq '.urls[].filename'
# For source install when wheels missing: pip install --no-binary :all: numpy
# Use system Python packages: poetry config virtualenvs.options.system-site-packages true poetry env use system
# Check current environment: poetry env info ```
Step 8: Manage Private Package Conflicts
```bash # Add private repository: poetry source add --priority=primary myrepo https://my.pypi.org/simple/
# Configure credentials: poetry config http-basic.myrepo username password
# Or use token: poetry config pypi-token.myrepo my-token-value
# Add package from private repo: poetry add --source myrepo private-package
# Check sources: poetry source show
# For conflicting package names: # Use explicit source in pyproject.toml: [tool.poetry.dependencies] private-package = {version = "^1.0", source = "myrepo"}
# Remove duplicate from PyPI if exists: poetry remove public-same-name-package
# Clear private repo cache: poetry cache clear myrepo --all
# Regenerate lock: poetry lock ```
Step 9: Fix Hash Mismatch and Sync Lock File
```bash # Check if lock file matches pyproject.toml: poetry lock --check
# If mismatch, regenerate: poetry lock
# For persistent mismatch: # 1. Remove lock file: rm poetry.lock
# 2. Remove .toml hash: sed -i '/^content-hash/d' pyproject.toml
# 3. Generate fresh lock: poetry lock
# 4. Verify hash: poetry lock --check # Output: Lock file is valid
# For corrupted lock file: # 1. Backup: cp poetry.lock poetry.lock.bak
# 2. Remove: rm poetry.lock
# 3. Regenerate: poetry lock
# 4. Compare differences: diff poetry.lock poetry.lock.bak
# Validate lock file format: python -c "import tomllib; tomllib.load(open('poetry.lock', 'rb'))" ```
Step 10: Implement Proper Dependency Management Workflow
```bash # Create standardized workflow script: cat << 'EOF' > scripts/poetry-resolve.sh #!/bin/bash
echo "=== Poetry Conflict Resolution ==="
# 1. Clear caches echo "Clearing caches..." poetry cache clear pypi --all 2>/dev/null || true
# 2. Check current state echo "Checking dependencies..." poetry show --tree
# 3. Validate pyproject.toml echo "Validating pyproject.toml..." python -c "import tomllib; tomllib.load(open('pyproject.toml', 'rb'))" || exit 1
# 4. Remove old lock echo "Removing old lock file..." rm -f poetry.lock
# 5. Fresh lock echo "Generating new lock file..." poetry lock -vvv || { echo "Lock failed. Trying with relaxed solver..." poetry config solver.use-poetry-solver false poetry lock }
# 6. Verify lock echo "Verifying lock file..." poetry lock --check || exit 1
# 7. Install echo "Installing dependencies..." poetry install --sync
echo "=== Resolution Complete ===" EOF
chmod +x scripts/poetry-resolve.sh
# Run resolution: ./scripts/poetry-resolve.sh
# Standard pyproject.toml structure: cat << 'EOF' > pyproject.toml.template [tool.poetry] name = "myproject" version = "0.1.0" description = "Project description" authors = ["Author <author@example.com>"]
[tool.poetry.dependencies] python = "^3.10" # Core dependencies with loose constraints requests = "^2.28" # Not ==2.28.0 flask = "^2.0" # Let transitive dependencies resolve themselves
[tool.poetry.group.dev.dependencies] pytest = "^7.0" black = "^23.0"
[build-system] requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" EOF
# Best practices for version constraints: # ^1.2.3 = >=1.2.3, <2.0.0 (allows minor/patch updates) # ~1.2.3 = >=1.2.3, <1.3.0 (allows patch updates only) # >=1.2.3 = any version >= 1.2.3 # 1.2.* = any version in 1.2.x series EOF ```
Poetry Dependency Checklist
| Check | Command | Expected |
|---|---|---|
| Lock valid | poetry lock --check | No errors |
| No conflicts | poetry show --tree | No conflicts shown |
| Version compatible | poetry show pkg | Compatible range |
| Hash match | lock --check | Valid lock |
| Cache clear | poetry cache clear | Cache purged |
| Install success | poetry install | All packages installed |
Verify the Fix
```bash # After resolving conflicts:
# 1. Generate lock file poetry lock # Output: Resolving dependencies... (complete)
# 2. Verify no conflicts poetry show --tree # Output: All dependencies resolved correctly
# 3. Check lock file validity poetry lock --check # Output: Lock file is valid and up to date
# 4. Install dependencies poetry install # Output: Installing dependencies... Complete
# 5. Test imports work poetry run python -c "import requests; print(requests.__version__)" # Output: 2.31.0 (or expected version)
# 6. Run tests poetry run pytest # All tests pass with new dependencies
# 7. Verify environment poetry env info # Path and Python version correct
# 8. Check dependency versions match expectations poetry show requests urllib3 # Both at compatible versions
# Compare before/after: # Before: SolverProblemError, version solving failed # After: Dependencies resolved, install successful ```
Related Issues
- [Fix Python Pip Install Failed](/articles/fix-python-pip-install-failed)
- [Fix Python Requirements Conflict](/articles/fix-python-requirements-conflict)
- [Fix Python Module Not Found Error](/articles/fix-python-import-module-not-found-error-deep)