iflytek/astron-agent · error · CustomException
20201
20201
Error message
Flow ID not found
What it means
The layout update endpoint looks up the Flow row by numeric id (session.query(Flow).filter_by(id=int(flow_id)).first()) and raises FLOW_NOT_FOUND_ERROR (20201) when no row matches. The DSL validated fine, but there is no persisted flow with that id to update.
Solutions
- Verify the flow_id exists: run SELECT id FROM flow WHERE id = <id> against the configured database.
- Refresh the flow list from the API and use a current id; if the flow was deleted, recreate it.
- Check DB connection config (host/database name) — the id may exist in a different environment.
- Confirm you are passing the flow's numeric id, not an app_id or other identifier.
Example fix
# before
await update_layout(flow_id="999999", dsl=dsl) # stale id
# after
flows = await list_flows()
if any(str(f.id) == flow_id for f in flows):
await update_layout(flow_id=flow_id, dsl=dsl)
else:
flow_id = await create_flow(dsl=dsl) Defensive patterns
Strategy: try-catch
Validate before calling
if session.query(Flow).filter_by(id=int(flow_id)).first() is None:
raise ValueError(f"flow {flow_id} does not exist") Type guard
def flow_exists(session, flow_id) -> bool:
return session.query(Flow).filter_by(id=int(flow_id)).first() is not None Try / catch
try:
await update_layout(flow_id=flow_id, dsl=dsl)
except CustomException as e:
if e.code == CodeEnum.FLOW_NOT_FOUND_ERROR.code:
flow_id = await create_flow(dsl=dsl) # recreate or refresh id
else:
raise Prevention
- Refresh flow lists after deletions instead of caching ids long-term
- Use the same environment/database for reads and writes
- Verify you pass the flow's numeric id, not app_id or a slug
When it happens
Trigger: PUT/update on the flow layout route with a flow_id that does not exist in the flows table; int(flow_id) succeeds (numeric string) but the row was deleted or belongs to another environment/database.
Common situations: Client keeps a flow_id from a deleted flow; environment mismatch (dev id used against prod DB); id vs string/other-key confusion (passing app_id as flow_id); database pointed at wrong schema via config.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/fb13ac29aa977a4f.
Report an issue: GitHub.
Appendix: source
Thrown at core/workflow/api/v1/flow/layout.py:185
span = Span()
m = Meter()
with span.start(
attributes={"flow_id": flow_id},
) as current_span:
try:
await current_span.add_info_event_async(f"update start: {flow_id}")
del_flow_by_id(flow_id)
sparkflow_protocol = sanitize_protocol_document_for_use(flow.data)
if sparkflow_protocol:
if isinstance(sparkflow_protocol, str):
sparkflow_protocol = json.loads(sparkflow_protocol)
WorkflowEngineFactory.create_engine(
WorkflowDSL.model_validate(sparkflow_protocol.get("data")),
current_span,
)
db_flow = session.query(Flow).filter_by(id=int(flow_id)).first()
if not db_flow:
raise CustomException(CodeEnum.FLOW_NOT_FOUND_ERROR)
flow_service.update(session, db_flow, flow)
m.in_success_count()
return Resp.success(None, span.sid)
except ValidationError as err:
validation_err = _protocol_validation_exception(err)
current_span.record_exception(validation_err)
m.in_error_count(validation_err.code, span=current_span)
return Resp.error(
validation_err.code,
validation_err.message,
span.sid,
)
except CustomException as err:
current_span.record_exception(err)
m.in_error_count(err.code, span=current_span)
return Resp.error(err.code, err.message, span.sid)
except Exception as e:View on GitHub (pinned to 5e758547a8)