{"record":{"id":"7ec3f3c290ec341e","repo":"affaan-m/ECC","slug":"user-not-found-user-id","errorCode":null,"errorMessage":"User not found: {user_id}","messagePattern":"User not found: (.+?)","errorType":"exception","errorClass":"NotFoundError","httpStatus":null,"severity":"warning","filePath":"skills/python-patterns/SKILL.md","lineNumber":194,"sourceCode":"\n```python\nclass AppError(Exception):\n    \"\"\"Base exception for all application errors.\"\"\"\n    pass\n\nclass ValidationError(AppError):\n    \"\"\"Raised when input validation fails.\"\"\"\n    pass\n\nclass NotFoundError(AppError):\n    \"\"\"Raised when a requested resource is not found.\"\"\"\n    pass\n\n# Usage\ndef get_user(user_id: str) -> User:\n    user = db.find_user(user_id)\n    if not user:\n        raise NotFoundError(f\"User not found: {user_id}\")\n    return user\n```\n\n## Context Managers\n\n### Resource Management\n\n```python\n# Good: Using context managers\ndef process_file(path: str) -> str:\n    with open(path, 'r') as f:\n        return f.read()\n\n# Bad: Manual resource management\ndef process_file(path: str) -> str:\n    f = open(path, 'r')\n    try:\n        return f.read()","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/skills/python-patterns/SKILL.md#L176-L212","documentation":"A NotFoundError (subclass of AppError) raised by get_user() when db.find_user(user_id) returns a falsy value. It indicates the requested user does not exist in the data store. It is part of a custom exception hierarchy (AppError -> NotFoundError) intended for domain-level control flow.","triggerScenarios":"Calling get_user(user_id) with an id that has no matching row; the database lookup returned None, an empty result, or an empty ORM object; the id was deleted between lookup and access.","commonSituations":"Stale references in a URL after a user is deleted; race condition between delete and read; typo in an id passed from a route parameter; soft-deleted records excluded by a default query filter.","solutions":["Confirm the user_id exists in the data source with a direct query.","Check whether a soft-delete filter or tenant scope is excluding the row.","Handle the race: re-fetch or use a transaction if a concurrent delete is possible.","Catch NotFoundError at the handler/controller layer and map it to an HTTP 404 response rather than a 500."],"exampleFix":"# before\nuser = db.find_user(user_id)\nif not user:\n    raise NotFoundError(f\"User not found: {user_id}\")\n\n# after: distinguish not-found from deleted at the boundary\nuser = db.find_user(user_id, include_deleted=False)\nif user is None:\n    existing = db.find_user(user_id, include_deleted=True)\n    if existing is not None:\n        raise GoneError(f\"User deleted: {user_id}\")\n    raise NotFoundError(f\"User not found: {user_id}\")","handlingStrategy":"try-catch","validationCode":"def user_exists(user_id: str) -> bool:\n    return db.find_user(user_id) is not None\n\nif not user_exists(user_id):\n    return http_response(404, {\"error\": \"user not found\"})","typeGuard":"null","tryCatchPattern":"from myapp.errors import NotFoundError\ntry:\n    user = get_user(user_id)\nexcept NotFoundError:\n    return respond_404(user_id)\nelse:\n    return respond_200(user)","preventionTips":["Catch the domain NotFoundError only at the controller/handler boundary, not in the repository.","Map it to 404, not 500.","Avoid echoing whether an id exists to prevent enumeration."],"tags":["python","domain","not-found","orm"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}