Skip to content

[Bug]: /api/learning-path/<id>/analytics returns 500 on malformed stored progress - AttributeError not caught #1861

Description

@ionfwsrijan

Summary

GET /api/learning-path/<path_id>/analytics returns HTTP 500 when a stored learning path contains a malformed progress value. create_path/update_path accept arbitrary client JSON (no schema validation), and the analytics handler only catches ValueError/TypeError — a non-dict progress value raises an uncaught AttributeError.

Evidence

src/routes/main_routes.py:1182-1198:

for pid_str, stats in progress.items():
    try:
        project = find_project_by_id(int(pid_str))
        if not project:
            continue
        est = project.get("estimated_hours", 0)
        act = stats.get("actual_hours", 0)   # AttributeError if stats is not a dict
        total_est += est
        total_act += act
        if stats.get("completed"):
            comp_est += est
            comp_act += act
    except (ValueError, TypeError):
        continue

stats comes from progress = path_data.get("progress", {}), where path_data is whatever the client stored via create_path (line 937) / update_path (line 1009) — the payload is JSON-parsed and saved without validating that each progress value is a dict. If a value is, e.g., a string or list, stats.get(...) raises AttributeError, which the except (ValueError, TypeError) does not catch → unhandled → 500.

Impact

  • A single malformed entry in any stored learning path makes the analytics endpoint 500 for that path.
  • The 500 leaks through the global error handler instead of returning a 4xx; the stored data came from the client, so a user can trivially trigger it for their own path (and it breaks the "Learning Velocity" UI).

Suggested Fix

  • Add AttributeError to the caught exceptions, or validate the shape of each stats value (require a dict) before calling .get().
  • Validate the progress structure at write time (create_path/update_path) so bad payloads are rejected with 400 instead of stored.

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions