affaan-m/ECC · error · ConfigError

Config file not found: {path}

Error message

Config file not found: {path}

What it means

`load_config` catches `FileNotFoundError` from `open(path)` and re-raises it as a domain `ConfigError(f"Config file not found: {path}")` using exception chaining (`from e`). This is the 'specific exception handling' pattern — the alternative `except:` bare clause that returns `None` is shown as the anti-pattern.

Source

Thrown at skills/python-patterns/SKILL.md:151

        """Render the object to a string."""

def render_all(items: list[Renderable]) -> str:
    """Render all items that implement the Renderable protocol."""
    return "\n".join(item.render() for item in items)
```

## Error Handling Patterns

### Specific Exception Handling

```python
# Good: Catch specific exceptions
def load_config(path: str) -> Config:
    try:
        with open(path) as f:
            return Config.from_json(f.read())
    except FileNotFoundError as e:
        raise ConfigError(f"Config file not found: {path}") from e
    except json.JSONDecodeError as e:
        raise ConfigError(f"Invalid JSON in config: {path}") from e

# Bad: Bare except
def load_config(path: str) -> Config:
    try:
        with open(path) as f:
            return Config.from_json(f.read())
    except:
        return None  # Silent failure!
```

### Exception Chaining

```python
def process_data(data: str) -> Result:
    try:
        parsed = json.loads(data)

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Verify the path with `pathlib.Path(path).resolve()` and `is_file()` before calling, and log the resolved absolute path.
  2. Confirm the working directory the process runs from (containers often start in `/app`, not the repo root).
  3. For deployments, ensure the config file is mounted/copied into the image at the expected absolute path.
  4. Make the path required and absolute in production; fall back to a packaged default only for dev.

Example fix

# before
try:
    with open(path) as f:
        return Config.from_json(f.read())
except FileNotFoundError as e:
    raise ConfigError(f"Config file not found: {path}") from e

# after — resolve+validate before open, clearer message
from pathlib import Path
p = Path(path).expanduser().resolve()
if not p.is_file():
    raise ConfigError(f"Config file not found: {p} (cwd={Path.cwd()})")
with p.open() as f:
    return Config.from_json(f.read())
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path
def config_exists(path: str) -> bool:
    return Path(path).expanduser().is_file()

Type guard

null

Try / catch

try:
    cfg = load_config(path)
except ConfigError as e:
    if 'not found' in str(e):
        fall_back_to_packaged_default()
    raise

Prevention

When it happens

Trigger: Caller passes a path that does not exist on disk: wrong working directory, typo, missing config in deployment, env var pointing at `/etc/app/config.json` that was not mounted.

Common situations: Working directory differs between dev and container so the relative path resolves wrong. Helm/k8s forgot to mount the ConfigMap. CI runs from repo root but the app expects `./config/`. Path built from an env var that was unset, yielding `None` or `''`.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/1acfd73e8c9b5f76. Report an issue: GitHub.