{"record":{"id":"d0e35032529667f1","repo":"ComposioHQ/composio","slug":"not-a-file-file-please-provide-a-valid-file-pa","errorCode":null,"errorMessage":"Not a file: {file}. Please provide a valid file path.","messagePattern":"Not a file: (.+?)\\. Please provide a valid file path\\.","errorType":"exception","errorClass":"SDKFileNotFoundError","httpStatus":null,"severity":"error","filePath":"python/composio/core/models/_files.py","lineNumber":645,"sourceCode":"        # ``None`` to bypass the allowlist (manual upload APIs).\n        if file_upload_allowlist is not None:\n            assert_path_inside_upload_dirs(path_in, file_upload_allowlist)\n\n        assert_safe_local_file_upload_path(\n            path_in,\n            enabled=sensitive_file_upload_protection,\n            additional_deny_segments=file_upload_path_deny_segments,\n        )\n\n        # Handle as local file path\n        file = Path(path_in)\n        if not file.exists():\n            raise SDKFileNotFoundError(\n                f\"File not found: {file}. Please provide a valid file path.\"\n            )\n\n        if not file.is_file():\n            raise SDKFileNotFoundError(\n                f\"Not a file: {file}. Please provide a valid file path.\"\n            )\n\n        if not os.access(file, os.R_OK):\n            raise SDKFileNotFoundError(\n                f\"File not readable: {file}. Please check the file permissions.\"\n            )\n\n        mimetype = mimetypes.guess(file=file)\n        s3meta = _request_presigned_upload(\n            client,\n            filename=file.name,\n            md5=get_md5(file=file),\n            mimetype=mimetype,\n            tool=tool,\n            toolkit=toolkit,\n        )\n        upload(url=s3meta.new_presigned_url, file=file, mimetype=mimetype)","sourceCodeStart":627,"sourceCodeEnd":663,"githubUrl":"https://github.com/ComposioHQ/composio/blob/64b1b85502b1beeb2379e6c9e8bf1104504fa637/python/composio/core/models/_files.py#L627-L663","documentation":"After confirming the path exists, from_path checks is_file() and raises SDKFileNotFoundError when the path is a directory, FIFO, device, or other non-regular file. Regular files are required because the content is read and uploaded.","triggerScenarios":"Passing a directory path or special file (e.g. /dev/null is a file, but a mount point, named pipe, or directory like ./data triggers this) to FileModel.from_path.","commonSituations":"Globbing that returns directories; passing a folder expecting recursive upload; symlink chains resolving to a directory; accidentally passing a path with a trailing slash on some platforms.","solutions":["Check p.is_file() first and filter out directories in your selection logic.","If you meant to upload multiple files, iterate over p.iterdir() filtering is_file() and upload each.","Fix the path to point at the actual file."],"exampleFix":"# before\nfile = FileModel.from_path(client, Path('./reports'))  # directory -> raises\n# after\nfor f in sorted(Path('./reports').glob('*')):\n    if f.is_file():\n        FileModel.from_path(client, f)","handlingStrategy":"type-guard","validationCode":"from pathlib import Path\n\npaths = [p for p in Path('./data').glob('*') if p.is_file()]","typeGuard":"def is_regular_file(p) -> bool:\n    from pathlib import Path\n    f = Path(p)\n    return f.exists() and f.is_file()","tryCatchPattern":"from composio.core.models._files import SDKFileNotFoundError\n\ntry:\n    model = FileModel.from_path(client, p)\nexcept SDKFileNotFoundError as e:\n    if 'Not a file' in str(e):\n        for child in sorted(Path(p).glob('*')):\n            if child.is_file():\n                FileModel.from_path(client, child)\n    else:\n        raise","preventionTips":["Filter glob results with is_file() before uploading.","Expand directories yourself if batch upload is intended.","Check for trailing slashes and symlinks pointing at directories."],"tags":["python","filesystem","validation","upload"],"backgroundTag":"path-is-not-a-file","analyzedSha":"64b1b85502b1beeb2379e6c9e8bf1104504fa637","analyzedAt":"2026-08-28T15:39:33.623Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}