apache/beam · error · ValueError
Path %r must be S3 path.
Error message
Path %r must be S3 path.
What it means
S3FileSystem.split() raises ValueError('Path %r must be S3 path.') when the given path, after stripping whitespace, does not start with the s3:// prefix. Like join(), split() is S3-specific and refuses non-S3 URIs before computing (dirname, basename).
Source
Thrown at sdks/python/apache_beam/io/aws/s3filesystem.py:87
path = path.rstrip('/') + '/' + p.lstrip('/')
return path
def split(self, path):
"""Splits the given path into two parts.
Splits the path into a pair (head, tail) such that tail contains the last
component of the path and head contains everything up to that.
Head will include the S3 prefix ('s3://').
Args:
path: path as a string
Returns:
a pair of path components as strings.
"""
path = path.strip()
if not path.startswith(S3FileSystem.S3_PREFIX):
raise ValueError('Path %r must be S3 path.' % path)
prefix_len = len(S3FileSystem.S3_PREFIX)
last_sep = path[prefix_len:].rfind('/')
if last_sep >= 0:
last_sep += prefix_len
if last_sep > 0:
return (path[:last_sep], path[last_sep + 1:])
elif last_sep < 0:
return (path, '')
else:
raise ValueError('Invalid path: %s' % path)
def mkdirs(self, path):
"""Recursively create directories for the provided path.
Args:
path: string path of the directory structure that should be createdView on GitHub (pinned to 12126d8942)
Solutions
- Pass a full S3 URI beginning with 's3://' to split().
- Check path.startswith(S3FileSystem.S3_PREFIX) before splitting and route non-S3 paths to their own filesystem.
- Parse the scheme generically (urlparse / Beam's FileSystems) and dispatch per scheme instead of hardcoding S3FileSystem.
- Watch for accidental prefix removal — avoid path.lstrip('s3:/') style transformations.
Example fix
// before
S3FileSystem().split('/tmp/prefix/file.avro')
// after
S3FileSystem().split('s3://bucket/prefix/file.avro') Defensive patterns
Strategy: type-guard
Validate before calling
def is_s3_path(p: str) -> bool:
return isinstance(p, str) and p.strip().startswith('s3://') Type guard
def is_s3_path(p: str) -> bool:
return isinstance(p, str) and p.strip().startswith('s3://') Try / catch
try:
dirpath, filename = S3FileSystem().split(path)
except ValueError as e:
if 'must be S3 path' in str(e):
# dispatch to the filesystem matching the actual scheme
dirpath, filename = FileSystems.split(path)
else:
raise Prevention
- Verify path scheme before calling S3-specific helpers
- Use FileSystems.split/join for scheme-agnostic path handling
- Don't mix local staging paths with S3 paths in the same code path
When it happens
Trigger: Calling S3FileSystem().split('/local/file.txt'), split('bucket/file'), or a path with a malformed prefix ('s3:/x', 'S3://x' case-sensitivity) — directly or via code that splits result paths from copy/match operations.
Common situations: Passing local staging paths into S3-specific helpers; processing matched file paths whose scheme is not s3; pipeline sinks emitting paths in another scheme mixed with S3 code paths; accidental prefix stripping with lstrip or replace.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/287de8a2cc2b5b6e.
Report an issue: GitHub.