Skip to content

fix(api): replace assert with explicit exceptions in db_manager and u… - #6234

Open
shafeeq27edu-ai wants to merge 2 commits into
Agenta-AI:mainfrom
shafeeq27edu-ai:fix/assert-to-exceptions-db-manager
Open

fix(api): replace assert with explicit exceptions in db_manager and u…#6234
shafeeq27edu-ai wants to merge 2 commits into
Agenta-AI:mainfrom
shafeeq27edu-ai:fix/assert-to-exceptions-db-manager

Conversation

@shafeeq27edu-ai

Copy link
Copy Markdown
Contributor

Description

This PR replaces assert statements with explicit exceptions in production API code. assert is stripped when Python runs with -O (optimize), causing silent failures where None leaks into downstream functions.

Summary

What changed? Replaced assert workspace_id is not None with if workspace_id is None: raise ValueError(...) in db_manager.py, and assert user_id is not None with if user_id is None: raise HTTPException(...) in user_profile.py.

Why was this change needed? assert statements are completely removed by Python's -O optimization flag. In production deployments that use python -O, these guards vanish silently. The variables being asserted (workspace_id, user_id) are then passed as None to downstream database queries, causing 500 errors or data corruption.

What problem does it solve? Prevents silent failures under optimized Python execution. Makes validation explicit and guaranteed to run regardless of Python flags.

How the change addresses the root cause: By converting assert to if + raise, the validation becomes real runtime code that survives python -O. The exception types match the context: ValueError for service-layer functions, HTTPException for router handlers.

Testing

Verified locally

  • Verified the assert lines exist at the reported locations in db_manager.py and user_profile.py
  • Confirmed python -O strips assert statements (no output, no error)
  • Confirmed if + raise survives python -O and raises correctly
  • Ran ruff check on both files — passes with no errors
  • Ran ruff format on both files — no changes needed

Added or updated tests

N/A — This is a defensive coding fix with no behavior change under normal execution. Existing validation tests cover the functional path.

QA follow-up

N/A — The fix is self-contained and verified by Python's -O behavior.

Demo

Before Fix (assert stripped by python -O)

image

After Fix

image

@dosubot dosubot Bot added the size:S This PR changes 10-29 lines, ignoring generated files. label Aug 23, 2026
@vercel

vercel Bot commented Aug 23, 2026

Copy link
Copy Markdown

@shafeeq27edu-ai is attempting to deploy a commit to the agenta projects Team on Vercel.

A member of the Team first needs to authorize it.

@dosubot dosubot Bot added bug python Pull requests that update Python code labels Aug 23, 2026
@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 81476483-46fe-4096-b3cd-22294562ad0d

📥 Commits

Reviewing files that changed from the base of the PR and between 9d188fe and fd200a7.

📒 Files selected for processing (1)
  • api/oss/src/routers/user_profile.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • api/oss/src/routers/user_profile.py

Included review availability: Your plan provides up to 8 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Improved validation feedback for project lookups when the workspace identifier is missing.
    • Preserved clear error handling for requests involving profiles that cannot be found.

Walkthrough

The changes replace assertion-based workspace validation with an explicit ValueError. The user-not-found error argument receives a trailing comma without a behavior change.

Changes

Explicit error handling

Layer / File(s) Summary
Replace assertions with explicit exceptions
api/oss/src/services/db_manager.py, api/oss/src/routers/user_profile.py
get_project_by_workspace raises ValueError when workspace_id is None. The user-not-found error detail formatting changes without changing behavior.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Merge Risk: 🟡 Moderate · up to fd200

The PR makes validation reliable under optimized Python execution, but the missing-user path still appears able to return a 500 instead of the intended 404 because the database lookup raises first. This bounded correctness issue should be fixed or explicitly accepted before merging.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: replacing assertions with explicit exceptions in API code.
Description check ✅ Passed The description accurately explains the changes, motivation, affected files, validation behavior, and testing performed.
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: f83a0b0d-b3e4-4dbe-8b05-ca19bd9ec79f

📥 Commits

Reviewing files that changed from the base of the PR and between 753af38 and 5010985.

📒 Files selected for processing (2)
  • api/oss/src/routers/user_profile.py
  • api/oss/src/services/db_manager.py

Included review availability: Your plan provides up to 8 included reviews per hour; 5 remain after this review.

Comment on lines +47 to +51
if user is None:
raise HTTPException(
status_code=404,
detail="User not found. Please ensure that the user_id is specified correctly.",
)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- candidate files ---'
fd -i 'user_profile.py|db_manager.py' api
printf '%s\n' '--- endpoint context ---'
sed -n '1,110p' api/oss/src/routers/user_profile.py
printf '%s\n' '--- database helper context ---'
sed -n '1200,1280p' api/oss/src/services/db_manager.py
printf '%s\n' '--- NoResultFound handlers and usages ---'
rg -n -S 'NoResultFound|no_result_found|exception_handler|HTTPException' api/oss/src api/oss/tests api 2>/dev/null | head -n 300

Repository: Agenta-AI/agenta

Length of output: 36312


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- APIRouter and exception registration ---'
rg -n -S 'class APIRouter|APIRouter\s*=|add_exception_handler|NoResultFound|SQLAlchemyError|ExceptionMiddleware' api/oss/src api/oss/tests | head -n 250
printf '%s\n' '--- user profile tests ---'
sed -n '1,260p' api/oss/tests/legacy/old_tests/variants_user_profile_router/test_user_profile.py
printf '%s\n' '--- user profile remaining router code ---'
sed -n '100,165p' api/oss/src/routers/user_profile.py
printf '%s\n' '--- imports for NoResultFound ---'
rg -n -S 'from sqlalchemy.exc import .*NoResultFound|import NoResultFound|NoResultFound' api/oss/src/services/db_manager.py api/oss/src | head -n 120
printf '%s\n' '--- source verifier ---'
python3 - <<'PY'
from pathlib import Path
import ast

router_path = Path("api/oss/src/routers/user_profile.py")
db_path = Path("api/oss/src/services/db_manager.py")
router = ast.parse(router_path.read_text())
db = ast.parse(db_path.read_text())

def find_function(tree, name):
    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name == name:
            return node
    raise LookupError(name)

endpoint = find_function(router, "user_profile")
helper = find_function(db, "get_user_with_id")

calls_helper = any(
    isinstance(node, ast.Call)
    and isinstance(node.func, ast.Attribute)
    and node.func.attr == "get_user_with_id"
    for node in ast.walk(endpoint)
)
raises_no_result = any(
    isinstance(node, ast.Raise)
    and isinstance(node.exc, ast.Call)
    and isinstance(node.exc.func, ast.Name)
    and node.exc.func.id == "NoResultFound"
    for node in ast.walk(helper)
)
none_guard = any(
    isinstance(node, ast.If)
    and isinstance(node.test, ast.Compare)
    and len(node.test.ops) == 1
    and isinstance(node.test.ops[0], ast.Is)
    and isinstance(node.test.comparators[0], ast.Constant)
    and node.test.comparators[0].value is None
    for node in ast.walk(endpoint)
)
no_result_handler = "NoResultFound" in router_path.read_text()
print({
    "user_profile_calls_get_user_with_id": calls_helper,
    "get_user_with_id_raises_NoResultFound": raises_no_result,
    "user_profile_has_none_guard": none_guard,
    "user_profile_mentions_NoResultFound": no_result_handler,
})
PY

Repository: Agenta-AI/agenta

Length of output: 15668


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- focused exception-registration search ---'
rg -n -S -g '*.py' 'add_exception_handler|exception_handlers|NoResultFound|@.*exception' api/oss/src api/oss/tests || true
printf '%s\n' '--- custom router definition ---'
rg -n -S -g '*.py' 'class APIRouter|def APIRouter' api/oss/src
printf '%s\n' '--- focused test file ---'
wc -l api/oss/tests/legacy/old_tests/variants_user_profile_router/test_user_profile.py
cat -n api/oss/tests/legacy/old_tests/variants_user_profile_router/test_user_profile.py
printf '%s\n' '--- test-runner references ---'
rg -n -S 'py-run-tests|run-tests.py' README.md CONTRIBUTING.md api/Makefile api/pyproject.toml api 2>/dev/null | head -n 160 || true
printf '%s\n' '--- verifier result ---'
python3 - <<'PY'
from pathlib import Path
import ast

router_path = Path("api/oss/src/routers/user_profile.py")
db_path = Path("api/oss/src/services/db_manager.py")
router_text = router_path.read_text()
db_text = db_path.read_text()
router = ast.parse(router_text)
db = ast.parse(db_text)

def find_function(tree, name):
    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name == name:
            return node
    raise LookupError(name)

endpoint = find_function(router, "user_profile")
helper = find_function(db, "get_user_with_id")

calls_helper = any(
    isinstance(node, ast.Call)
    and isinstance(node.func, ast.Attribute)
    and node.func.attr == "get_user_with_id"
    for node in ast.walk(endpoint)
)
raises_no_result = any(
    isinstance(node, ast.Raise)
    and isinstance(node.exc, ast.Call)
    and isinstance(node.exc.func, ast.Name)
    and node.exc.func.id == "NoResultFound"
    for node in ast.walk(helper)
)
has_try_except_no_result = any(
    isinstance(node, ast.Try)
    and any(
        isinstance(handler.type, ast.Name) and handler.type.id == "NoResultFound"
        for handler in node.handlers
    )
    for node in ast.walk(endpoint)
)
has_none_guard = any(
    isinstance(node, ast.If)
    and isinstance(node.test, ast.Compare)
    and len(node.test.ops) == 1
    and isinstance(node.test.ops[0], ast.Is)
    and len(node.test.comparators) == 1
    and isinstance(node.test.comparators[0], ast.Constant)
    and node.test.comparators[0].value is None
    for node in ast.walk(endpoint)
)
print({
    "endpoint_calls_get_user_with_id": calls_helper,
    "helper_raises_NoResultFound": raises_no_result,
    "endpoint_catches_NoResultFound": has_try_except_no_result,
    "endpoint_has_none_guard": has_none_guard,
    "endpoint_mentions_NoResultFound": "NoResultFound" in router_text,
    "database_source_mentions_NoResultFound": "NoResultFound" in db_text,
})
PY

Repository: Agenta-AI/agenta

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -e
rg -n -S -g '*.py' 'add_exception_handler|exception_handlers|NoResultFound|@.*exception' api/oss/src api/oss/tests || true
rg -n -S -g '*.py' 'class APIRouter|def APIRouter' api/oss/src
cat -n api/oss/tests/legacy/old_tests/variants_user_profile_router/test_user_profile.py
rg -n -S 'py-run-tests|run-tests.py' README.md CONTRIBUTING.md api/Makefile api/pyproject.toml api 2>/dev/null | head -n 160 || true
python3 - <<'PY'
from pathlib import Path
import ast

router_path = Path("api/oss/src/routers/user_profile.py")
db_path = Path("api/oss/src/services/db_manager.py")
router_text = router_path.read_text()
db_text = db_path.read_text()
router = ast.parse(router_text)
db = ast.parse(db_text)

def find_function(tree, name):
    for node in ast.walk(tree):
        if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)) and node.name == name:
            return node
    raise LookupError(name)

endpoint = find_function(router, "user_profile")
helper = find_function(db, "get_user_with_id")

print({
    "endpoint_calls_get_user_with_id": any(
        isinstance(n, ast.Call) and isinstance(n.func, ast.Attribute)
        and n.func.attr == "get_user_with_id" for n in ast.walk(endpoint)
    ),
    "helper_raises_NoResultFound": any(
        isinstance(n, ast.Raise) and isinstance(n.exc, ast.Call)
        and isinstance(n.exc.func, ast.Name)
        and n.exc.func.id == "NoResultFound" for n in ast.walk(helper)
    ),
    "endpoint_catches_NoResultFound": any(
        isinstance(n, ast.Try)
        and any(isinstance(h.type, ast.Name) and h.type.id == "NoResultFound"
                for h in n.handlers)
        for n in ast.walk(endpoint)
    ),
})
PY

Repository: Agenta-AI/agenta

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- custom APIRouter ---'
cat -n api/oss/src/utils/common.py | sed -n '1,90p'
printf '%s\n' '--- exception utility definitions ---'
cat -n api/oss/src/utils/exceptions.py | sed -n '1,220p'
printf '%s\n' '--- application construction and handlers ---'
rg -l -S -g '*.py' 'FastAPI\(|add_exception_handler|exception_handlers' api/oss/src | while read -r f; do
  echo "--- $f"
  rg -n -S 'FastAPI\(|add_exception_handler|exception_handlers' "$f"
done
printf '%s\n' '--- exact test command references ---'
rg -n -S -g '!**/.venv/**' -g '!**/node_modules/**' 'py-run-tests|run-tests.py' . | head -n 120 || true
printf '%s\n' '--- route AST exception-flow verifier ---'
python3 - <<'PY'
from pathlib import Path
import ast

router_path = Path("api/oss/src/routers/user_profile.py")
db_path = Path("api/oss/src/services/db_manager.py")
router = ast.parse(router_path.read_text())
db = ast.parse(db_path.read_text())

def fn(tree, name):
    return next(
        n for n in ast.walk(tree)
        if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef)) and n.name == name
    )

endpoint = fn(router, "user_profile")
helper = fn(db, "get_user_with_id")

def has_call(node, attr):
    return any(
        isinstance(n, ast.Call) and isinstance(n.func, ast.Attribute)
        and n.func.attr == attr for n in ast.walk(node)
    )

def raises_name(node, name):
    return any(
        isinstance(n, ast.Raise)
        and isinstance(n.exc, ast.Call)
        and isinstance(n.exc.func, ast.Name)
        and n.exc.func.id == name for n in ast.walk(node)
    )

def catches_name(node, name):
    return any(
        isinstance(n, ast.Try)
        and any(
            isinstance(h.type, ast.Name) and h.type.id == name
            for h in n.handlers
        )
        for n in ast.walk(node)
    )

print("endpoint_calls_helper:", has_call(endpoint, "get_user_with_id"))
print("helper_raises_NoResultFound:", raises_name(helper, "NoResultFound"))
print("endpoint_catches_NoResultFound:", catches_name(endpoint, "NoResultFound"))
PY

Repository: Agenta-AI/agenta

Length of output: 22575


🏁 Script executed:

#!/bin/bash
set -e
cat -n api/oss/src/utils/common.py | sed -n '1,90p'
cat -n api/oss/src/utils/exceptions.py | sed -n '1,220p'
rg -l -S -g '*.py' 'FastAPI\(|add_exception_handler|exception_handlers' api/oss/src | while read -r f; do
  echo "--- $f"
  rg -n -S 'FastAPI\(|add_exception_handler|exception_handlers' "$f"
done
rg -n -S -g '!**/.venv/**' -g '!**/node_modules/**' 'py-run-tests|run-tests.py' . | head -n 120 || true
python3 - <<'PY'
from pathlib import Path
import ast

router = ast.parse(Path("api/oss/src/routers/user_profile.py").read_text())
db = ast.parse(Path("api/oss/src/services/db_manager.py").read_text())

def fn(tree, name):
    return next(n for n in ast.walk(tree)
                if isinstance(n, (ast.FunctionDef, ast.AsyncFunctionDef))
                and n.name == name)

endpoint = fn(router, "user_profile")
helper = fn(db, "get_user_with_id")

def has_call(node, attr):
    return any(isinstance(n, ast.Call)
               and isinstance(n.func, ast.Attribute)
               and n.func.attr == attr for n in ast.walk(node))

def raises_name(node, name):
    return any(isinstance(n, ast.Raise)
               and isinstance(n.exc, ast.Call)
               and isinstance(n.exc.func, ast.Name)
               and n.exc.func.id == name for n in ast.walk(node))

def catches_name(node, name):
    return any(isinstance(n, ast.Try)
               and any(isinstance(h.type, ast.Name) and h.type.id == name
                       for h in n.handlers)
               for n in ast.walk(node))

print("endpoint_calls_helper:", has_call(endpoint, "get_user_with_id"))
print("helper_raises_NoResultFound:", raises_name(helper, "NoResultFound"))
print("endpoint_catches_NoResultFound:", catches_name(endpoint, "NoResultFound"))
PY

Repository: Agenta-AI/agenta

Length of output: 22386


Handle NoResultFound around get_user_with_id().

get_user_with_id() raises NoResultFound for a missing user, so the user is None branch is unreachable. Catch the exception and raise the 404 HTTPException, or change the helper to return None. Add a regression test and run cd api && py-run-tests.

Source: Coding guidelines

…ser_profile

assert statements are stripped when Python runs with -O (optimize).
This caused silent failures where None leaked into downstream code.

- db_manager.py: assert workspace_id → if + raise ValueError
- user_profile.py: assert user_id → if + raise HTTPException

Verified with custom test script under both python and python -O.
No behavior change under normal execution — only makes the code
safe under python -O.
@shafeeq27edu-ai
shafeeq27edu-ai force-pushed the fix/assert-to-exceptions-db-manager branch from 5010985 to 9d188fe Compare August 24, 2026 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug python Pull requests that update Python code size:S This PR changes 10-29 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant