iflytek/astron-agent · error · CustomException
21601
21601
Error message
can not find main function
What it means
_parser_code_parameter parses the node's Python source with regex to find function signatures and locate the entry point. The engine specifically requires a function named `main`; if none of the matched function definitions is named main (or the regex finds no functions at all), CODE_BUILD_ERROR is raised with 'can not find main function'. The code node contract mandates a main entry function.
Solutions
- Rename the entry function to exactly `def main(...):` at module top level.
- Ensure main is a plain top-level def (not nested, not a method, no unusual formatting that breaks the regex).
- Check that `async def main` is supported for your workflow version; otherwise make it synchronous.
- Remove any syntax oddities (decorators on main, weird whitespace) and keep the signature simple.
- Debug by running the script locally to confirm it parses, then re-save the node.
Example fix
// before
def run(x):
return {"out": x}
// after
def main(x):
return {"out": x} Defensive patterns
Strategy: validation
Validate before calling
import re
src = code_node_source
matches = re.findall(r"def\s+(\w+)\s*\(([^)]*)\)", src)
if not any(name.strip() == "main" for name, _ in matches):
raise ValueError("code node must define a top-level 'def main(...)'") Type guard
def has_main_function(src: str) -> bool:
import re
return bool(re.search(r"^\s*def\s+main\s*\(", src, re.M)) Try / catch
try:
params = code_node._parser_code_parameter(code)
except CustomException as e:
if e.err_code == CodeEnum.CODE_BUILD_ERROR:
log.error("code node missing main(): rename entry function to main")
raise Prevention
- Always name the code node entry function exactly `main` at top level
- Avoid renaming main during refactors without updating the node
- Port scripts from other platforms by wrapping logic in def main(...)
- Keep signatures simple (no decorators/nesting) so the parser matches
When it happens
Trigger: The code node's script defines only differently-named functions (e.g. `run`, `handler`, `execute`); main is nested inside a class or guarded in a way the regex doesn't match; the script is empty or contains only imports/comments; syntax variations (async def, decorators) break the regex match for the name group.
Common situations: Users pasting scripts from other platforms (which use different entry-point conventions); refactoring renamed main and forgot to update it; snippets with `if __name__ == '__main__'` blocks but no main def; templates using async def main which the parser may not recognize.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12).
Data as JSON: /api/errors/1572792915c2a6a6.
Report an issue: GitHub.
Appendix: source
Thrown at core/workflow/engine/nodes/code/code_node.py:296
:param python_code: Python code string containing function definitions
:return: List of parameter names from the main function
:raises CustomException: If main function is not found in the code
"""
# Remove comment lines to avoid parsing issues
python_code = "\n".join(
line for line in python_code.splitlines() if not line.strip().startswith("#")
)
# Regex pattern to match function definitions with optional type hints
re_pattern = r"def\s+(\w+)\s*\(([^)]*)\)\s*(?:->\s*[\w\[\],\s]*)?:"
re_matches = re.findall(re_pattern, python_code, re.DOTALL)
re_parameter: str | None = None
# Find the main function specifically
for re_match in re_matches:
if re_match[0].strip() == "main":
re_parameter = re_match[1].strip()
break
if re_parameter is None:
raise CustomException(
CodeEnum.CODE_BUILD_ERROR,
err_msg="can not find main function",
cause_error="can not find main function",
)
# Split parameters and extract parameter names (remove type hints)
re_params = re_parameter.split(",")
variables = []
for re_param in re_params:
re_param = re_param.strip()
if re_param:
# Remove type hints if present (everything after colon)
re_param = re_param.split(":")[0].strip()
variables.append(re_param)
return variables
class CodeSandboxConfig(BaseModel):
model_config = ConfigDict(populate_by_name=True)View on GitHub (pinned to 5e758547a8)