Troubleshooting
Configuration Not Loading
Files Aren't Being Picked Up
Check which files are being searched:
from utilityhub_config.errors import ConfigValidationError
try:
settings, _ = load_settings(Config)
except ConfigValidationError as e:
print("Checked files:")
for file in e.checked_files:
print(f" - {file}")
Solution: Move your config files to one of the checked locations.
Wrong App Name Derived
By default, the app name comes from your model class:
class MyAppConfig(BaseModel): # App name derived: "myappconfig"
pass
This looks for ~/.config/myappconfig/myappconfig.toml.
Solution: Specify explicit app name:
settings, _ = load_settings(MyAppConfig, app_name="myapp")
# Now looks for ~/.config/myapp/myapp.toml
Environment Variables Not Working
Variable Not Found
Environment variables must match field names (uppercase):
class Config(BaseModel):
api_key: str # Looks for API_KEY environment variable
db_url: str # Looks for DB_URL environment variable
Check:
echo $API_KEY # Is it set?
echo $DB_URL # Is it set?
Using Prefix and Variables Ignored
With a prefix, only prefixed variables are checked:
# With prefix="MYAPP", only looks for:
# MYAPP_API_KEY, MYAPP_DB_URL
# Does NOT look for API_KEY or DB_URL
settings, _ = load_settings(
Config,
env_prefix="MYAPP"
)
Solution: Either set prefixed variables or remove the prefix.
Case Sensitivity
Environment variable names are case-sensitive on Linux/Mac:
export api_key="value" # Won't work
export API_KEY="value" # Correct
Type Conversion Errors
"value is not a valid integer" Error
Values from .env and environment are strings and must be convertible:
class Config(BaseModel):
port: int
# .env
PORT=8080 # Correct - string "8080" converts to int
PORT=invalid # ERROR - can't convert "invalid" to int
Solution: Ensure values can be converted to the declared type.
Boolean Values Not Converting
Boolean strings must be recognized values:
# Recognized as True:
DEBUG=true
DEBUG=True
DEBUG=1
# Recognized as False:
DEBUG=false
DEBUG=False
DEBUG=0
Tip: Use lowercase true/false in .env files for consistency.
List/Array Values
TOML and YAML support lists naturally:
# TOML
tags = ["api", "web", "service"]
# YAML
tags:
- api
- web
- service
For .env files, individual items don't convert to lists. Use a quoted list string:
# .env - use a quoted list string
TAGS='["api","web","service"]'
Validation Errors
Missing Required Field
If a field has no default and isn't provided:
class Config(BaseModel):
api_key: str # No default!
# Error: "api_key" is required
settings, _ = load_settings(Config)
Solution: Either provide a default or set it in config/environment:
# Option 1: Add default
class Config(BaseModel):
api_key: str = "default_key"
# Option 2: Provide in config file or environment
os.environ["API_KEY"] = "my_key"
Constraint Violations
Pydantic validators are enforced:
from pydantic import BaseModel, Field
class Config(BaseModel):
port: int = Field(gt=0, lt=65536) # Valid port range
# .env
PORT=-1 # ERROR: less than 0
Solution: Ensure values satisfy constraints.
Precedence Issues
Settings Not Being Overridden
Remember the precedence order (lowest to highest):
- Defaults
- Global config
- Project config
- .env
- Environment variables
- Runtime overrides
If a higher level is set, lower levels are ignored:
class Config(BaseModel):
debug: bool = False
# Global config: debug = true
# Project config: debug = false # This wins, overrides global
Solution: Check which level is setting your value:
settings, metadata = load_settings(Config)
source = metadata.get_source("debug")
print(f"debug came from: {source.source}")
File Format Issues
TOML Syntax Errors
TOML has strict syntax:
# Wrong - unquoted string
app_name = myapp
# Correct
app_name = "myapp"
# Wrong - invalid key
my-setting = "value"
# Correct - use underscore or alphanumeric
my_setting = "value"
YAML Indentation
YAML is indentation-sensitive:
# Wrong - inconsistent indentation
database:
host: localhost
port: 5432
# Correct
database:
host: localhost
port: 5432
Use spaces (not tabs) for indentation.
Nested Structure Mismatch
Ensure nested config matches your model:
class DatabaseConfig(BaseModel):
host: str
port: int
class Config(BaseModel):
database: DatabaseConfig
# Correct structure
[database]
host = "localhost"
port = 5432
# Wrong - missing nesting
database_host = "localhost" # Doesn't match model
Debugging Configuration Loading
Enable Detailed Logging
import logging
# Enable debug logging
logging.basicConfig(level=logging.DEBUG)
from utilityhub_config import load_settings
settings, metadata = load_settings(Config)
Print All Sources
settings, metadata = load_settings(Config)
print("Configuration sources:")
for field, source in metadata.per_field.items():
print(f" {field}:")
print(f" Source: {source.source}")
print(f" Path: {source.source_path}")
print(f" Value: {source.raw_value}")
Check Files Searched
from utilityhub_config.errors import ConfigValidationError
try:
settings, _ = load_settings(Config)
except ConfigValidationError as e:
print("Files searched:")
for file in e.checked_files:
print(f" {file}")
Common Patterns for Solutions
Problem: "Settings work in dev, not in production"
Cause: Different app name, missing environment variables, or wrong config directory.
Solution:
# Explicitly specify everything
settings, _ = load_settings(
Config,
app_name="myapp", # Same across all environments
cwd=Path("/etc/myapp"), # Production config location
env_prefix="MYAPP" # Consistent prefix
)
Problem: "I can't tell where a setting came from"
Solution:
settings, metadata = load_settings(Config)
# Check a specific field
source = metadata.get_source("api_key")
if source:
print(f"Source: {source.source}")
print(f"Path: {source.source_path}")
print(f"Value: {source.raw_value}")
Problem: "I'm accidentally overriding production config locally"
Cause: Environment variables or .env file overriding intended config.
Solution: Be explicit about precedence:
# Avoid loading .env in production
import os
if not os.getenv("PRODUCTION"):
# Only load .env in development
settings, _ = load_settings(Config)
Or use explicit overrides:
overrides = {}
if os.getenv("ENV") == "production":
overrides = {
"database_url": "postgresql://prod.db/app",
"debug": False
}
settings, _ = load_settings(Config, overrides=overrides)
Getting Help
If you can't resolve the issue:
- Check file locations - Print
checked_fileslist - Check sources - Use
metadata.per_fieldto see where values come from - Enable logging - Set
logging.basicConfig(level=logging.DEBUG) - Verify types - Ensure config values match Pydantic model types
- Check precedence - Remember that environment variables override .env