Skip to content

Decide request-parameter coverage, error translation, and S3 retry codes #1342

Open
1 of 4 issues completed

Description

@laughingman7743

Part of #1327 (design problems 6 and 7 of the audit report).

Summary

Decide and document which request parameters reach which requests, how S3 errors are translated, and which layer retries S3 errors.

Current state (766d79d)

  1. s3_additional_kwargs reaches open, pipe_file and put_file only (documented), not touch, cat_file, cp_file/copy/mv, setxattr or rm: users migrating from s3fs who put SSE or SSE-C settings there get different results from touch or cp than from pipe, and cat(p) fails where open(p).read() works. Per call, rm_file ignores parameters that rm forwards, mkdir drops everything but acl and region_name, and touch(path, Body=b"x") writes data although it creates an empty object.
  2. Append mode updates the request with appended.to_api_repr(), which overrides the caller's ContentType, StorageClass and Metadata and forwards SSE and object-lock fields of the existing object (s3.py:2803); documented as keeping the old object's metadata.
  3. Error translation: only mkdir turns ParamValidationError into ValueError; cat_file and clear_multipart_uploads re-parse e.__cause__ because the translated OSError carries no S3 code; exists() maps 403 to True for buckets and raises PermissionError for keys; copy_object_annotation reads its payload without closing it or translating errors.
  4. Retries: the default RetryConfig retries ThrottlingException and TooManyRequestsException, which are Athena's codes. S3's SlowDown, ServiceUnavailable, InternalError and RequestTimeout are retried only by botocore, so stop_retries_on and unstoppable_retries govern no S3 retry unless the user adds S3 codes to a policy that a connection shares with Athena. docs/filesystem.md says only that requests are "sent with the retry policy". Retrying in both layers is acceptable; the question is which codes PyAthena's layer retries for S3 and how that is documented.

Proposal (for decision)

  1. Document, or extend, the coverage of s3_additional_kwargs, and make rm_file, mkdir and touch consistent with it.
  2. Let explicit caller parameters win over the appended object's in append mode, or document the precedence.
  3. Carry the S3 error code on translated errors, add core options such as missing_ok for aborts and an InvalidRange mode for get_object, and pick one ParamValidationError policy.
  4. Give S3 its own default retry codes, separate from Athena's, or document that botocore owns S3 retries.

Activity

  1. laughingman7743 commented on Nov 7, 2026

    @laughingman7743
    MemberAuthor

    Input for item 1 from #1333 / #1362 (verified offline at 2a60ecc and at the PR heads).

    Copy and move: the expansion and pairing requests send no parameters

    #1362 makes the requests that read a copy's source send the source's authorization parameters: the source lookup of _copy_file, mv's conflict lookup and deletion, and setxattr's HeadObject. Before those requests, copy/mv with string paths expand and pair the paths, and those requests send nothing. In a requester-pays bucket, or for an SSE-C source, a copy/mv therefore still fails before the fixed lookups run. This holds even for a single file: copy("s3://b/a", "s3://b/b", RequestPayer="requester") sends HeadObject(a), HeadObject(b) and ListObjectsV2(b/) without RequestPayer. Lists of both sources and destinations are paired without these requests. docs/filesystem.md states both facts.

    The requests involved:

    • The source expansion: expand_path → glob/find listings, and the exists(p) of a version path in a recursive copy.
    • fsspec's own expand_path, which calls self.exists(p) without kwargs after an empty recursive find.
    • The directory checks of the sources (_copy_pairs/fsspec copy: isdir(p)).
    • The directory check of the destination.

    Why the straightforward fix was reverted

    87ef858 (reverted in #1362) let _ls_dirs/_find/find/expand_path honor the lookup parameters. It sent the source parameters to the source expansion and the destination's lookup parameters to the destination check, routing a parameterized copy through _copy_pairs. The independent review found:

    1. fsspec's copy() passes the CopyObject kwargs to expand_path → find. Once find honors lookup names, the destination's ExpectedBucketOwner is sent with the source listing. Example: copy("s3://src/data/*", ["s3://dst/r"], ExpectedSourceBucketOwner=A, ExpectedBucketOwner=B), where path2 is a list, so the copy is not routed.
    2. Routing a parameterized aio glob copy through the sync expansion lists a wider prefix (reports/ instead of reports/2026-), which a prefix-scoped IAM policy denies.
    3. fsspec's async _expand_path rejects the extra keywords with TypeError.
    4. A listing with parameters must bypass the listing cache, both reading and writing, and that needs its own tests.

    Design questions for this item:

    • Should listings (ls/find/glob/walk/du) accept lookup parameters at all? If they do, by which channel, so that fsspec's forwarding of the CopyObject kwargs cannot leak destination-side names into source requests?
    • Does a parameterized copy/mv expand with its own code (sync and aio) instead of fsspec's, keeping the aio glob prefix?
    • How do listing results made with parameters relate to the listing cache?
  2. laughingman7743 commented on Nov 7, 2026

    @laughingman7743
    MemberAuthor

    Decisions (2026-11-07), after an offline audit at 29cc1d3. Every item still held at that commit. Each sub-issue has the current state and the decision:

    1. Send the authorization parameters of s3_additional_kwargs with every filesystem request #1372: Every request sends the authorization parameters of s3_additional_kwargs (RequestPayer, ExpectedBucketOwner, SSECustomer*), listings included, filtered per operation. This covers the copy/mv expansion above and what remains of Pass the source's SSE-C and RequestPayer parameters to the lookups of copies and setxattr #1333. The same PR fixes the per-call gaps of rm_file, mkdir, and touch.
    2. Let the caller's parameters take precedence in append mode #1373: In append mode the caller's parameters win, and the existing object's object-lock fields are not forwarded.
    3. Unify ParamValidationError translation and document S3 error codes #1374: ParamValidationError → ValueError everywhere (breaking). S3ClientError.code_of is documented as the public way to get the S3 code. exists() on a 403 stays as it is and is documented. copy_object_annotation closes and translates its payload read. Core missing_ok/InvalidRange options are deferred.
    4. Give the S3 filesystem its own default retry codes #1375: The filesystem gets its own default S3 retry codes. They are added only while a connection's retry policy is still the default.

    One PR per sub-issue, starting with #1372.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions