{"record":{"id":"8444a64ae29c2db7","repo":"ComposioHQ/composio","slug":"refusing-to-build-a-path-from-an-empty-or-non-stri","errorCode":null,"errorMessage":"Refusing to build a path from an empty or non-string {label}: {value!r}","messagePattern":"Refusing to build a path from an empty or non-string (.+?): (.+?)","errorType":"validation","errorClass":"UnsafePathComponentError","httpStatus":null,"severity":"error","filePath":"python/composio/utils/safe_path.py","lineNumber":101,"sourceCode":"        sep = os.sep\n        parent_with_sep = parent_str if parent_str.endswith(sep) else parent_str + sep\n        return child_str.startswith(parent_with_sep)\n    except OSError:\n        return False\n\n\ndef assert_safe_path_component(value: str, *, label: str = \"path component\") -> str:\n    \"\"\"Return ``value`` unchanged if it is safe to use as a single path\n    component, else raise.\n\n    Fails closed. Rejects traversal (``..``), separators of either platform,\n    absolute paths, drive letters, NUL bytes, reserved device names, and\n    anything outside :data:`SAFE_COMPONENT_REGEX`.\n\n    :raises UnsafePathComponentError: when ``value`` is unsafe.\n    \"\"\"\n    if not isinstance(value, str) or not value:\n        raise UnsafePathComponentError(\n            f\"Refusing to build a path from an empty or non-string {label}: {value!r}\"\n        )\n\n    # `PureWindowsPath` treats both `/` and `\\` as separators, so a single check\n    # catches `../x` and `..\\x` regardless of the host platform. A slug crafted\n    # for a Windows target must not slip through on a POSIX build machine.\n    as_windows_path = PureWindowsPath(value)\n    if len(as_windows_path.parts) != 1 or as_windows_path.anchor:\n        raise UnsafePathComponentError(\n            f\"Refusing to build a path from a {label} containing path separators \"\n            f\"or a drive letter: {value!r}\"\n        )\n\n    if len(value) > MAX_COMPONENT_LENGTH:\n        raise UnsafePathComponentError(\n            f\"Refusing to build a path from a {label} longer than \"\n            f\"{MAX_COMPONENT_LENGTH} characters: {value[:32]!r}... \"\n            f\"({len(value)} characters)\"","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/utils/safe_path.py#L83-L119","documentation":"assert_safe_path_component refuses to build a path from a value that is not a non-empty string (None, int, empty ''). All untrusted path components (slugs, IDs) must be non-empty strings before they can join a filesystem path.","triggerScenarios":"Calling secure_join(root, component) where component is None (e.g. a missing API field), an empty string, or a non-string like an int ID.","commonSituations":"API responses with missing/optional slug or ID fields passed straight into secure_join; deserialized JSON where the field is absent rather than empty.","solutions":["Check for None/empty before joining and skip or default the operation","Coerce non-string identifiers with str(...) only if that is genuinely the intended value","Fix the caller that produced None (missing field in request payload)"],"exampleFix":"# before\npath = secure_join(root, tool.slug)  # slug is None\n# after\nif not tool.slug:\n    raise ValueError(\"tool slug missing\")\npath = secure_join(root, tool.slug)","handlingStrategy":"type-guard","validationCode":"def usable_component(v):\n    return isinstance(v, str) and len(v) > 0","typeGuard":"def is_safe_component(v) -> bool:\n    return isinstance(v, str) and bool(v)","tryCatchPattern":"from composio.exceptions import UnsafePathComponentError\ntry:\n    p = secure_join(root, slug)\nexcept UnsafePathComponentError:\n    slug = fallback_slug()  # e.g. derived from a hash\n    p = secure_join(root, slug)","preventionTips":["Check required API fields for presence before path building","Never pass raw optional fields into path helpers"],"tags":["path-traversal","validation","python","security"],"backgroundTag":"path-component-validation-failed","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}