Graph factories re-bind the server-injected __pregel_runtime config key: 500 AttributeError '_ReadRuntime' has no 'override' on /assistants/{id}/graph (Studio preview broken)
Summary
GET /assistants/{assistant_id}/graph returns 500 with AttributeError: '_ReadRuntime' object has no attribute 'override' for every graph, which surfaces in LangGraph Studio as "Failed to preview graph". Reproducible on a fresh checkout with the dependency set resolved by this repo's own uv.lock (langgraph 1.2.8, langgraph-api 0.10.3, langgraph-sdk 0.4.2) — and still present after upgrading to langgraph-api 0.11.2 (latest), so it is not just version skew.
Root cause
langgraph_api.graph.get_graph() injects its server runtime into the config before invoking a graph factory (langgraph-api ≥ 0.11 behavior gated on USE_RUNTIME_CONTEXT_API, i.e. any langgraph >= 0.6):
# langgraph_api/graph.py
config["configurable"][CONFIG_KEY_RUNTIME] = server_runtime # "__pregel_runtime"
...
value = invoke_factory(value, graph_id, config, server_runtime)Every open-swe factory then ends by re-binding that same config onto the compiled graph:
# agent/server.py, agent/reviewer.py, agent/analyzer.py, agent/chat.py, agent/scheduler.py
).with_config(config)This statically bakes the factory-time ServerRuntime (_ReadRuntime / _ExecutionRuntime from langgraph_sdk.runtime) into the graph's bound config under the private CONFIG_KEY_RUNTIME key. When langgraph later prepares tasks (including the draw path used by aget_graph), it treats that object as its own Runtime:
# langgraph/pregel/_algo.py::prepare_single_task
runtime = cast(Runtime, configurable.get(CONFIG_KEY_RUNTIME, DEFAULT_RUNTIME))
runtime = runtime.override(...) # _ReadRuntime defines no override() -> AttributeError_ExecutionRuntime also lacks override(), so the same failure threatens the real execution path, not only Studio previews.
Minimal repro (no server needed)
import asyncio
from langgraph.graph import END, START, StateGraph
from langgraph_sdk.runtime import _ReadRuntime
from typing_extensions import TypedDict
class State(TypedDict):
x: int
async def main() -> None:
builder = StateGraph(State)
builder.add_node("n", lambda s: s)
builder.add_edge(START, "n")
builder.add_edge("n", END)
# What langgraph_api.graph.get_graph() injects before calling a factory,
# and what the open-swe factories then re-bind via `.with_config(config)`.
config = {"configurable": {"__pregel_runtime": _ReadRuntime(access_context="assistants.read", user=None, store=None)}}
graph = builder.compile().with_config(config)
await graph.aget_graph() # what GET /assistants/{id}/graph does
asyncio.run(main())Output with langgraph==1.2.8 / langgraph-sdk==0.4.2 (also 1.2.10 / 0.4.2):
File ".../langgraph/pregel/_algo.py", line 691, in prepare_single_task
runtime = runtime.override(
AttributeError: '_ReadRuntime' object has no attribute 'override'Full-app repro: uv sync, langgraph dev, open Studio, load any graph — or curl the graph endpoint and watch the server log.
Suggested fix
Strip the private key (or avoid re-binding the incoming config wholesale) before .with_config(...). In our fork we fixed it with one line in the shared factory wrapper, which covers agent, reviewer, analyzer, and chat (plus one line in scheduler, the only factory not routed through the wrapper):
# agent/utils/tracing.py::traced_graph_factory
def _strip_server_runtime(config):
configurable = config.get("configurable")
if not configurable or "__pregel_runtime" not in configurable:
return config
return {**config, "configurable": {k: v for k, v in configurable.items() if k != "__pregel_runtime"}}
graph = await factory(_strip_server_runtime(config))After this, all five /assistants/{id}/graph endpoints return 200 and Studio previews render; no other behavior change observed (the docs describe ServerRuntime as factory-time context, and CONFIG_KEY_RUNTIME lives under langgraph._internal).
Happy to open a PR with the wrapper-level fix if you'd take it.
Environment
- open-swe @ current
main(pattern present atagent/utils/tracing.py:20and all nine.with_config(config)factory sites) langgraph 1.2.8/langgraph-api 0.10.3/langgraph-sdk 0.4.2(repouv.lock); also reproduced withlanggraph-api 0.11.2andlanggraph 1.2.10- Python 3.12, macOS (arm64) — nothing platform-specific involved
Source: langchain-ai/open-swe