{"record":{"id":"2c561291736fb3ed","repo":"Significant-Gravitas/AutoGPT","slug":"graph-id-does-not-match-id-in-uri","errorCode":null,"errorMessage":"Graph ID does not match ID in URI","messagePattern":"Graph ID does not match ID in URI","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"warning","filePath":"autogpt_platform/backend/backend/api/features/v1.py","lineNumber":1801,"sourceCode":"            graph_id, user_id=user_id, organization_id=ctx.org_id\n        )\n    }\n\n\n@v1_router.put(\n    path=\"/graphs/{graph_id}\",\n    summary=\"Update graph version\",\n    tags=[\"graphs\"],\n    dependencies=[Security(requires_user)],\n)\nasync def update_graph(\n    graph_id: str,\n    graph: graph_db.Graph,\n    user_id: Annotated[str, Security(get_user_id)],\n    ctx: Annotated[RequestContext, Security(get_request_context)],\n) -> UpdateGraphResponse:\n    if graph.id and graph.id != graph_id:\n        raise HTTPException(400, detail=\"Graph ID does not match ID in URI\")\n\n    existing_versions = await graph_db.get_graph_all_versions(graph_id, user_id=user_id)\n    if not existing_versions:\n        raise HTTPException(404, detail=f\"Graph #{graph_id} not found\")\n\n    graph.version = max(g.version for g in existing_versions) + 1\n    current_active_version = next((v for v in existing_versions if v.is_active), None)\n\n    graph = graph_db.make_graph_model(graph, user_id)\n    graph.reassign_ids(user_id=user_id, reassign_graph_id=False)\n    graph.validate_graph(for_run=False)\n\n    # If this new version is going to be active, validate node credentials\n    # BEFORE persisting so a credential issue can't leave a half-saved version\n    # behind. before_graph_activate may also clear stale optional credentials —\n    # those edits must be persisted, hence the pre-save call.\n    if graph.is_active:\n        graph = await before_graph_activate(graph, user_id=user_id)","sourceCodeStart":1783,"sourceCodeEnd":1819,"githubUrl":"https://github.com/Significant-Gravitas/AutoGPT/blob/9c8bb5550f446ba5d3046b78896578742495b3cf/autogpt_platform/backend/backend/api/features/v1.py#L1783-L1819","documentation":"Raised (400) by PUT /graphs/{graph_id} when the request body carries a non-null graph.id that differs from the graph_id in the URI. The API treats the path parameter as authoritative; a body/URI mismatch is rejected to prevent accidentally overwriting a different graph.","triggerScenarios":"PUT /graphs/aaa-... with body {\"id\": \"bbb-...\", ...} — typically a frontend bug that saved graph A's draft into state and submitted it against graph B's URL, or a copy-paste of a graph JSON into an update call against another graph's route.","commonSituations":"Builder UI state desync when switching between graphs quickly; 'duplicate then edit' flows that keep the source graph's id in the payload; API clients templating the URL and body from different sources.","solutions":["Make the body's id match the URI (or omit graph.id entirely — null is allowed and the URI wins).","In the frontend, key graph-editing state by graph_id so switching graphs resets the payload.","For duplication flows use the dedicated create endpoint instead of PUT with a foreign id."],"exampleFix":"// before\nawait api.updateGraph(graphId, { ...staleGraph, id: staleGraph.id });\n\n// after\nawait api.updateGraph(graphId, { ...staleGraph, id: graphId });\n// or omit id: await api.updateGraph(graphId, { ...graph, id: null });","handlingStrategy":"validation","validationCode":"if (payload.id && payload.id !== routeGraphId) {\n  throw new Error(`Body graph id ${payload.id} != URI ${routeGraphId}`);\n}\nawait api.updateGraph(routeGraphId, { ...payload, id: routeGraphId });","typeGuard":"const idsAgree = (bodyId: string | null | undefined, uriId: string) => !bodyId || bodyId === uriId;","tryCatchPattern":"catch (e) { if (e.response?.status === 400 && /does not match/.test(e.response.data.detail)) { resyncGraphStateFromServer(); } else throw e; }","preventionTips":["Derive the URL and the payload id from the same piece of state; never mix sources.","Reset editing state when switching graphs in the builder.","Omit graph.id from update payloads unless you explicitly round-trip it from a GET of the same ID."],"tags":["api","graphs","http-400","validation","id-mismatch"],"backgroundTag":null,"analyzedSha":"9c8bb5550f446ba5d3046b78896578742495b3cf","analyzedAt":"2026-08-14T17:17:21.957Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}