{"record":{"id":"8384a5466f4866ba","repo":"langchain-ai/deepagents","slug":"invalid-skill-source-expected-str-or-str-str-t","errorCode":null,"errorMessage":"Invalid skill source: expected str or (str, str) tuple, got {source!r}","messagePattern":"Invalid skill source: expected str or \\(str, str\\) tuple, got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"libs/deepagents/deepagents/middleware/skills.py","lineNumber":175,"sourceCode":"name). The label is rendered as `**{label} Skills**` in the system\nprompt; do not include the trailing \"Skills\" yourself.\n\"\"\"\n\n\ndef _validate_tuple_source(source: tuple[object, ...]) -> None:\n    \"\"\"Raise `TypeError` if a tuple source is not a `(str, str)` pair.\n\n    Catches the near-miss shapes at construction time so the traceback\n    points at the caller rather than at a later `IndexError` inside the\n    middleware or a silently-coerced non-string path downstream.\n    \"\"\"\n    if (\n        len(source) != 2  # noqa: PLR2004  # SkillSource tuple is exactly (path, label)\n        or not isinstance(source[0], str)\n        or not isinstance(source[1], str)\n    ):\n        msg = f\"Invalid skill source: expected str or (str, str) tuple, got {source!r}\"\n        raise TypeError(msg)\n\n\ndef _source_path(source: SkillSource) -> str:\n    \"\"\"Return just the path component of a source.\"\"\"\n    if isinstance(source, str):\n        return source\n    _validate_tuple_source(source)\n    return source[0]\n\n\ndef _truncate_skill_load_warning(error: str) -> str:\n    \"\"\"Cap a skill loading warning before placing it in the model prompt.\"\"\"\n    if len(error) <= MAX_SKILL_LOAD_WARNING_LENGTH:\n        return error\n    length = MAX_SKILL_LOAD_WARNING_LENGTH - len(_SKILL_LOAD_WARNING_TRUNCATION_SUFFIX)\n    return f\"{error[:length]}{_SKILL_LOAD_WARNING_TRUNCATION_SUFFIX}\"\n\n","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/langchain-ai/deepagents/blob/a1af029e6e73cb17c36bff823d227747b28e91e1/libs/deepagents/deepagents/middleware/skills.py#L157-L193","documentation":"Skill sources may be given as a plain path string or a (path, label) two-element string tuple. `_validate_tuple_source` rejects anything else — wrong tuple length, non-string elements, or other types — with a TypeError describing the received value. This keeps path/label bookkeeping (self.sources / self.source_labels) index-aligned.","triggerScenarios":"SkillsMiddleware(..., sources=[(\"/skills\",)]) (length-1 tuple); [(\"/skills\", 42)]; passing a Path object or a 3-tuple.","commonSituations":"Using pathlib.Path instead of str; packing an extra metadata field into the tuple; YAML/JSON config decoding tuples as lists of mixed types.","solutions":["Use str(pathlib_path) instead of a Path object.","Ensure each tuple is exactly (str_path, str_label), e.g. (\"/skills\", \"team-skills\").","Pass a plain string if no custom label is needed.","Validate config-derived sources with a quick check before constructing the middleware."],"exampleFix":"// before\nsources=[Path(\"./skills\")]\n// after\nsources=[str(Path(\"./skills\")), ]  # or (\"./skills\", \"local\")","handlingStrategy":"type-guard","validationCode":"def norm_source(s):\n    if isinstance(s, str):\n        return s\n    if isinstance(s, tuple) and len(s) == 2 and all(isinstance(x, str) for x in s):\n        return s\n    raise TypeError(f\"bad skill source: {s!r}\")\nsources = [norm_source(s) for s in cfg_sources]","typeGuard":"def is_skill_source(v: object) -> TypeGuard[str | tuple[str, str]]:\n    if isinstance(v, str):\n        return True\n    return isinstance(v, tuple) and len(v) == 2 and all(isinstance(x, str) for x in v)","tryCatchPattern":"try:\n    mw = SkillsMiddleware(backend=b, sources=sources)\nexcept TypeError as e:\n    if \"Invalid skill source\" in str(e):\n        mw = SkillsMiddleware(backend=b, sources=[str(s) for s in sources])\n    else:\n        raise","preventionTips":["Convert pathlib.Path to str before adding to sources.","Use tuples of exactly (path, label), both strings.","Validate config-decoded sources (JSON yields lists, not tuples).","Centralize source normalization in one helper."],"tags":["python","skills","validation","type-error"],"backgroundTag":"invalid-parameter-type","analyzedSha":"a1af029e6e73cb17c36bff823d227747b28e91e1","analyzedAt":"2026-08-29T11:43:24.718Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}