{"record":{"id":"501b1566c05119e3","repo":"iflytek/astron-agent","slug":"skillid-is-required","errorCode":null,"errorMessage":"skillId is required","messagePattern":"skillId is required","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"core/workflow/engine/nodes/agent/agent_node.py","lineNumber":145,"sourceCode":"    class Resource(BaseModel):\n        path: str = Field(min_length=1)\n        name: str = Field(default=\"\")\n        downloadUrl: str = Field(default=\"\")\n        fileExt: str = Field(default=\"\")\n        fileSize: int = Field(default=0)\n\n    skillId: str = Field(..., min_length=1)\n    name: str = Field(min_length=1, max_length=128)\n    description: str = Field(min_length=0, max_length=1024)\n    downloadUrl: str = Field(default=\"\")\n    resources: List[Resource] = Field(default_factory=list)\n    sandbox: Dict[str, Any] = Field(default_factory=dict)\n\n    @field_validator(\"skillId\", mode=\"before\")\n    @classmethod\n    def normalize_skill_id(cls, value: Any) -> str:\n        if value is None:\n            raise ValueError(\"skillId is required\")\n        return str(value)\n\n    @field_validator(\"description\", \"downloadUrl\", mode=\"before\")\n    @classmethod\n    def normalize_optional_string_fields(cls, value: Any) -> str:\n        return \"\" if value is None else str(value)\n\n    @field_validator(\"sandbox\", mode=\"before\")\n    @classmethod\n    def remove_untrusted_sandbox_credentials(cls, value: Any) -> dict[str, Any]:\n        if not isinstance(value, dict):\n            return {}\n        sanitized = dict(value)\n        for key in (\n            \"provider\",\n            \"apiKey\",\n            \"api_key\",\n            \"timeoutSeconds\",","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/iflytek/astron-agent/blob/5e758547a83371a5a4b29dadf4ac03e8dd527635/core/workflow/engine/nodes/agent/agent_node.py#L127-L163","documentation":"Pydantic field_validator (mode=\"before\") on the Skill model's skillId: a null skillId is rejected before any coercion, since str(None) would silently produce \"None\". Skill entries in an agent node must carry a concrete skill identifier.","triggerScenarios":"Providing a Skill object with skillId: null or omitting skillId entirely when populating the agent node's skill list — usually from upstream data where skill metadata was incomplete.","commonSituations":"Marketplace/plugin skill records missing an id field; deserializing skill lists from an API response where skillId is absent; test fixtures with placeholder skills.","solutions":["Ensure every skill entry includes a valid skillId before constructing the agent node config","Filter out skills with null/missing skillId from the list before validation","Check the upstream skill API response for missing ids and fix the source of skill data"],"exampleFix":"// before\n{\"name\": \"my-skill\", \"skillId\": null}\n// after\n{\"name\": \"my-skill\", \"skillId\": \"skill-456\"}","handlingStrategy":"validation","validationCode":"def clean_skills(skills: list[dict]) -> list[dict]:\n    return [s for s in skills if s.get(\"skillId\") is not None]","typeGuard":"def has_skill_id(s: dict) -> bool:\n    return isinstance(s, dict) and s.get(\"skillId\") is not None","tryCatchPattern":"from pydantic import ValidationError\ntry:\n    skills = [Skill.model_validate(s) for s in raw_skills]\nexcept ValidationError as e:\n    log.error(\"skill missing skillId: %s\", e.errors())\n    raise","preventionTips":["Filter incomplete skill records before building agent node configs","Ensure the skill marketplace API always returns skillId","Add a save-time check for skill completeness"],"tags":["pydantic","validation","agent-node","skill"],"backgroundTag":"missing-required-argument","analyzedSha":"5e758547a83371a5a4b29dadf4ac03e8dd527635","analyzedAt":"2026-09-12T08:03:51.356Z","contentChangedAt":"2026-09-12T08:03:51.356Z","schemaVersion":2},"datasetVersion":"2026-09-19T12:17:13.211Z"}