You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
An object whose key ends in / and has content (for example dir/ with body abc) cannot be read through info()-based paths. fsspec's _strip_protocol() removes the trailing / (s3://bucket/dir/ and s3://bucket/dir both become bucket/dir). info() then heads dir, finds no object, and reports a directory of size 0, so:
info("s3://bucket/dir/") returns type="directory", size=0, and isfile() returns False.
cat_file("s3://bucket/dir/", start=-2) returns the whole object b"abc" on master. Negative offsets are resolved against the size of 0, which produces a Range that S3 ignores. With Clamp byte ranges to the object in cat_file() and S3File reads #987, it raises FileNotFoundError.
cat_file() without a range, or with non-negative offsets, uses the exact key and reads the object (b"abc", b"a" for [0:1]).
ls("s3://bucket/dir/", detail=True) lists the object as a file of size 3 named bucket/dir/, but that name cannot be passed back to info() or open().
Most keys ending in / are zero-byte folder markers (for example, from the S3 console's "Create folder"), and treating them as directories is the usual interpretation. The problem is limited to such keys that hold data, and to the inconsistency between ls() and info()/open().
Expected: decide whether a key ending in / is addressable as a file. If it is, info(), open() and negative offsets reach it by its exact key. If it is not, ls() should not return it as a file entry, and the limitation should be documented.
Reproduction
Checked on live S3 with three objects: p/only/ (b"abc"), p/slash/ (b"abc") and p/slash/child (b"x").
s3fs 2026.9.0 on the same objects: info() reports a directory of size 0, isfile() is False, open().read() returns b"", and ls() returns the same file entry. cat_file(p, start=-2) returns b"bc", because s3fs sends a suffix range (bytes=-2) without looking up the size.
This needs a maintainer decision before implementation:
Keep keys ending in / as directories (fsspec path model, as s3fs does for info()/open()). Then exclude such keys from ls() file entries, or document them as unreadable except through cat_file(). Optionally, resolve cat_file(start=-N) with a suffix range (bytes=-N) as s3fs does, which needs no size lookup.
Make them addressable as files.info(), open() and the directory cache would have to distinguish dir/ from dir, contrary to fsspec's normalization. This touches info, ls, dircache keys, invalidate_cache, S3File paths and pickling, and diverges from s3fs.
Problem
An object whose key ends in
/and has content (for exampledir/with bodyabc) cannot be read throughinfo()-based paths. fsspec's_strip_protocol()removes the trailing/(s3://bucket/dir/ands3://bucket/dirboth becomebucket/dir).info()then headsdir, finds no object, and reports a directory of size 0, so:info("s3://bucket/dir/")returnstype="directory",size=0, andisfile()returnsFalse.open("s3://bucket/dir/", "rb").read()returnsb""on master. With Clamp byte ranges to the object in cat_file() and S3File reads #987 (S3File reads and cat_file() return the whole object for empty or out-of-bounds ranges #970), it raisesFileNotFoundError, because opening a prefix is now rejected.cat_file("s3://bucket/dir/", start=-2)returns the whole objectb"abc"on master. Negative offsets are resolved against the size of 0, which produces a Range that S3 ignores. With Clamp byte ranges to the object in cat_file() and S3File reads #987, it raisesFileNotFoundError.cat_file()without a range, or with non-negative offsets, uses the exact key and reads the object (b"abc",b"a"for[0:1]).ls("s3://bucket/dir/", detail=True)lists the object as afileof size 3 namedbucket/dir/, but that name cannot be passed back toinfo()oropen().Most keys ending in
/are zero-byte folder markers (for example, from the S3 console's "Create folder"), and treating them as directories is the usual interpretation. The problem is limited to such keys that hold data, and to the inconsistency betweenls()andinfo()/open().Expected: decide whether a key ending in
/is addressable as a file. If it is,info(),open()and negative offsets reach it by its exact key. If it is not,ls()should not return it as a file entry, and the limitation should be documented.Reproduction
Checked on live S3 with three objects:
p/only/(b"abc"),p/slash/(b"abc") andp/slash/child(b"x").p/only/(no children) gives the same results.s3fs 2026.9.0 on the same objects:
info()reports a directory of size 0,isfile()isFalse,open().read()returnsb"", andls()returns the same file entry.cat_file(p, start=-2)returnsb"bc", because s3fs sends a suffix range (bytes=-2) without looking up the size.Environment
aa0fc914and PR Clamp byte ranges to the object in cat_file() and S3File reads #987 head02f8ebdd, fsspec 2026.9.0, s3fs 2026.9.0 for comparison, Python 3.13.1.Proposed fix (optional)
This needs a maintainer decision before implementation:
/as directories (fsspec path model, as s3fs does forinfo()/open()). Then exclude such keys fromls()file entries, or document them as unreadable except throughcat_file(). Optionally, resolvecat_file(start=-N)with a suffix range (bytes=-N) as s3fs does, which needs no size lookup.info(),open()and the directory cache would have to distinguishdir/fromdir, contrary to fsspec's normalization. This touchesinfo,ls,dircachekeys,invalidate_cache,S3Filepaths and pickling, and diverges from s3fs.