verdier opened a new issue, #42567:
URL: https://github.com/apache/superset/issues/42567
### Bug description
The built-in MCP service's `generate_chart` is not safe under modest
concurrency. From 5 concurrent calls, most calls raise `DetachedInstanceError`
on the freshly created `Slice` — **after the chart has already been
committed**. The client receives an error for a chart that exists.
For an LLM agent this is the worst possible shape: the tool reports failure,
the agent retries, and each retry creates another chart. Duplicate charts
accumulate silently.
```
superset.mcp_service.chart.tool.generate_chart:Chart generation failed:
Instance <Slice at 0x7ac59a7dc310> is not bound to a Session;
attribute refresh operation cannot proceed
File "/app/superset/mcp_service/chart/tool/generate_chart.py", line 828,
in generate_chart
File ".../sqlalchemy/orm/attributes.py", line 487, in __get__
File ".../sqlalchemy/orm/state.py", line 712, in _load_expired
File ".../sqlalchemy/orm/loading.py", line 1369, in load_scalar_attributes
sqlalchemy.orm.exc.DetachedInstanceError
```
`generate_chart.py:828` is the completion log, which reads `chart.id` after
the transaction has committed and the instance has been expired/detached:
```python
await ctx.info(
"Chart generation completed successfully: chart_id=%s,
execution_time_ms=%s"
% (chart.id if chart else None, ...)
)
```
### How to reproduce
`apache/superset:6.1.0-py311`, `fastmcp==3.4.5`, `superset mcp run`,
`stateless_http=True`, stock SQLAlchemy pool. N concurrent `generate_chart`
calls against one physical dataset via `asyncio.gather`, then read back the
`slices` table.
| concurrency | successful responses | rows actually created |
|---|---|---|
| 1 | 1/1 | 1 |
| 2 | 2/2 | 2 |
| 5 | 2/5 | 5 |
| 10 | 4/10 | 10 |
| 20 | 1/20 | **3** |
Two things worth noting:
- **The highest fully clean concurrency is 2.** Two users asking an agent
for a chart at the same time is enough to reach it.
- At concurrency 20 the writes themselves start disappearing (3 rows for 20
calls), so this is not purely a response-serialisation problem.
Assertions are read back from the metadata DB rather than from the MCP
client result, because the error middleware returns the failure as `Error: ...`
text while the SDK's `result.is_error` stays false — a green client flag can
hide a total failure here, which may be worth a separate look.
### Not caused by local customisation
We ran this while evaluating the built-in MCP for a deployment that needs
per-user identity. To rule ourselves out, we ran the identical workload on
**stock 6.1 with no customisation at all**, in single-user mode
(`MCP_DEV_USERNAME`):
- stock, unmodified: **5/40** successful responses
- with our own identity bridge installed: **19/40**
So the unmodified configuration performs *worse*. The defect is in the
built-in MCP itself.
### `generate_dashboard` is unaffected, and that looks deliberate
`generate_dashboard` passes 40/40 under the same load. It re-queries the
user by id inside the tool's own session before use
(`superset/mcp_service/dashboard/tool/generate_dashboard.py:301-317`), with a
comment describing exactly this class of problem. So the pattern appears to be
understood — it just has not been applied to the chart path.
This also makes `generate_dashboard` a misleading tool to benchmark
concurrency with: it produces a false green.
### Question about `master`
`master` has since restructured this path — `chart_id` is captured
immediately after `command.run()`, and `db.session.refresh(chart)` is wrapped
in a guarded `try/except SQLAlchemyError`. But `chart.id` and
`chart.slice_name` are still read afterwards, outside that guard.
We have not been able to test `master`, so we cannot say whether the
concurrency case is resolved there. Two questions:
1. Is this considered fixed on `master`?
2. If so, is the fix expected in 6.2 — and would a 6.1.x backport be
considered? The identity chain in `superset/mcp_service/auth.py`
(`_resolve_user_from_jwt_context` and the `claims`-first
`default_user_resolver`) landed just after the 6.1.0 tag as well, so 6.1.0
users currently have neither.
Happy to run any diagnostic against 6.1.0, or to test a candidate patch — we
have a scripted reproduction and can turn it around quickly.
### Superset version
6.1.0
### Python version
3.11
### Node version
Not applicable
### Browser
Not applicable
### Additional context
Reported by [Veremes](https://www.veremes.com), evaluating the built-in MCP
service for a production Superset deployment.
### Checklist
- [x] I have searched Superset docs and Slack and didn't find a solution to
my problem.
- [x] I have searched the GitHub issue tracker and didn't find a similar bug
report.
- [x] I have checked Superset's logs for errors and if I found a relevant
Python stacktrace, I included it here as text or in a screenshot.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]