{"record":{"id":"08c329fa8a2dc0f1","repo":"Significant-Gravitas/AutoGPT","slug":"unauthorized-update-attempt-for-profile-existing","errorCode":null,"errorMessage":"Unauthorized update attempt for profile {existing_profile.id} by user {user_id}","messagePattern":"Unauthorized update attempt for profile (.+?) by user (.+?)","errorType":"exception","errorClass":"DatabaseError","httpStatus":500,"severity":"critical","filePath":"autogpt_platform/backend/backend/api/features/store/db.py","lineNumber":1266,"sourceCode":"                )\n                return store_model.ProfileDetails.from_db(created_profile)\n            except prisma.errors.UniqueViolationError:\n                # A concurrent request (or get_or_create_user) created the\n                # Profile first. Re-fetch and fall through to update it with the\n                # submitted data rather than failing the save.\n                existing_profile = await prisma.models.Profile.prisma().find_first(\n                    where={\"userId\": user_id}\n                )\n                if not existing_profile:\n                    raise\n\n        # Verify that the user is authorized to update this profile\n        if existing_profile.userId != user_id:\n            logger.error(\n                f\"Unauthorized update attempt for profile {existing_profile.id} \"\n                f\"by user {user_id}\"\n            )\n            raise DatabaseError(\n                f\"Unauthorized update attempt for profile {existing_profile.id} \"\n                f\"by user {user_id}\"\n            )\n\n        logger.debug(f\"Updating existing profile for user {user_id}\")\n        # Prepare update data, only including non-None values\n        update_data = {}\n        if profile.name is not None:\n            update_data[\"name\"] = profile.name\n        if profile.username is not None:\n            update_data[\"username\"] = username\n        if profile.description is not None:\n            update_data[\"description\"] = profile.description\n        if profile.links is not None:\n            update_data[\"links\"] = profile.links\n        if profile.avatar_url is not None:\n            update_data[\"avatarUrl\"] = profile.avatar_url\n","sourceCodeStart":1248,"sourceCodeEnd":1284,"githubUrl":"https://github.com/Significant-Gravitas/AutoGPT/blob/9c8bb5550f446ba5d3046b78896578742495b3cf/autogpt_platform/backend/backend/api/features/store/db.py#L1248-L1284","documentation":"Raised in update_profile when the Profile row selected for update belongs to a different userId than the authenticated user. This is an IDOR/authorization guard. Because the lookup filters by userId=user_id, reaching this branch requires the row's userId to have changed between fetch and check (race), or a code path calling update_profile with mismatched arguments — i.e. an internal invariant break, logged at error level with both profile ID and user ID. Note it is raised as DatabaseError, though NotAuthorizedError would be semantically correct.","triggerScenarios":"Two concurrent profile updates where one changes the row's ownership; a direct call to update_profile with a profile object whose existing row resolves to another user; data corruption where a Profile row's userId was edited mid-request.","commonSituations":"Almost never seen in practice from the HTTP API; observed in tests calling the service function with mismatched fixtures, or after manual DB edits to Profile.userId.","solutions":["Treat as a security-relevant incident: capture the logged profile ID and user ID pair.","Inspect Profile.userId in the DB for the listed profile — if it changed unexpectedly, audit who/what changed it.","If you are calling update_profile directly, ensure the user_id you pass is the authenticated user, not the profile owner parameter.","Consider patching the code to raise NotAuthorizedError instead of DatabaseError so the API returns 403 rather than 500."],"exampleFix":"# before\nraise DatabaseError(f\"Unauthorized update attempt for profile {existing_profile.id} by user {user_id}\")\n# after\nfrom backend.util.exceptions import NotAuthorizedError\nraise NotAuthorizedError(f\"Unauthorized update attempt for profile {existing_profile.id}\")","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"from backend.util.exceptions import DatabaseError\n\ntry:\n    await store_db.update_profile(user_id, profile)\nexcept DatabaseError as e:\n    if 'Unauthorized update attempt' in str(e):\n        security_log.alert('profile ownership race', user_id=user_id)\n        raise Forbidden() from e  # surface as 403, not 500\n    raise","preventionTips":["Always derive user_id from the authenticated session, never from request bodies.","Alert on this specific message — it indicates an ownership invariant break, not noise.","Consider patching the raise site to NotAuthorizedError so clients get 403 semantics."],"tags":["authorization","security","profile","store","race-condition"],"backgroundTag":null,"analyzedSha":"9c8bb5550f446ba5d3046b78896578742495b3cf","analyzedAt":"2026-08-14T17:17:21.957Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}