{"record":{"id":"e7c71ec97e46cdf6","repo":"windmill-labs/windmill","slug":"invalid-s3-object-s3-object-r-expected-an-s3","errorCode":null,"errorMessage":"Invalid s3 object {s3_object!r}: expected an s3://<storage>/<key> URI (e.g. 's3:///{s3_object}' for key {s3_object!r} in the default storage) or S3Object(s3=<key>)","messagePattern":"Invalid s3 object (.+?): expected an s3://<storage>/<key> URI \\(e\\.g\\. 's3:///(.+?)' for key (.+?) in the default storage\\) or S3Object\\(s3=<key>\\)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python-client/wmill/wmill/client.py","lineNumber":2331,"sourceCode":"\ndef parse_s3_object(s3_object: S3Object | str) -> S3Object:\n    \"\"\"Parse S3 object from a `s3://<storage>/<key>` URI string (`s3:///<key>`\n    for the default storage) or S3Object format. Any other string raises\n    rather than falling back to an auto-generated key: an auto key is\n    requested by omitting the object, and a fallback would silently misplace\n    the upload on any typo.\n    \"\"\"\n    if isinstance(s3_object, str):\n        match = re.match(r'^s3://([^/]*)/(.+)$', s3_object)\n        if match:\n            return S3Object(s3=match.group(2), storage=match.group(1) or None)\n        if s3_object.startswith(\"s3://\"):\n            raise ValueError(\n                f\"Invalid s3 object URI {s3_object!r}: expected \"\n                \"s3://<storage>/<key> with a non-empty key \"\n                \"(s3:///<key> for the default storage)\"\n            )\n        raise ValueError(\n            f\"Invalid s3 object {s3_object!r}: expected an s3://<storage>/<key> \"\n            f\"URI (e.g. 's3:///{s3_object}' for key {s3_object!r} in the default \"\n            \"storage) or S3Object(s3=<key>)\"\n        )\n    else:\n        return s3_object\n\n    \n\ndef parse_variable_syntax(s: str) -> Optional[str]:\n    \"\"\"Parse variable syntax from string.\"\"\"\n    if s.startswith(\"var://\"):\n        return s[6:]\n    return None\n\n\ndef append_to_result_stream(text: str) -> None:\n    \"\"\"Append a text to the result stream.","sourceCodeStart":2313,"sourceCodeEnd":2349,"githubUrl":"https://github.com/windmill-labs/windmill/blob/e474e8803ce2ff5c2df09a58dab51d45f5c922ca/python-client/wmill/wmill/client.py#L2313-L2349","documentation":"The catch-all ValueError of the S3 object coercion helper: the value is neither a well-formed s3:// string URI nor an S3Object instance — e.g. a local path, a plain dict, an int, None, or an S3Object-like value from an older SDK version.","triggerScenarios":"Passing '/tmp/data.csv', {'s3': 'key'}, None, or any non-S3Object/non-URI value into functions accepting S3Object | str.","commonSituations":"Mixing local-file APIs with S3 APIs; constructing an object incorrectly after a library upgrade changed the S3Object type; passing values that are paths rather than s3:// URIs.","solutions":["Pass an S3Object instance: S3Object(s3='key', storage=None)","Pass a valid URI string 's3://<storage>/<key>' or 's3:///<key>'","Convert plain keys explicitly: S3Object(s3=my_key)","Type-check inputs (and reject None/local paths) before calling"],"exampleFix":"// before\nwm.read_s3_file(\"/tmp/data.csv\")  # local path, not an S3 object\n// after\nwm.read_s3_file(\"s3:///data.csv\")  # or S3Object(s3=\"data.csv\")","handlingStrategy":"type-guard","validationCode":"import re\nif not isinstance(s3_object, (str, S3Object)):\n    raise TypeError(f\"Expected s3:// URI or S3Object, got {type(s3_object).__name__}\")","typeGuard":"def is_s3_object_like(v: object) -> bool:\n    import re\n    if isinstance(v, S3Object):\n        return True\n    return isinstance(v, str) and bool(re.match(r'^s3://([^/]*)/(.+)$', v))","tryCatchPattern":"try:\n    obj = coerce_s3_object(value)\nexcept ValueError:\n    obj = S3Object(s3=str(value))  # only if value is a plain key","preventionTips":["Type-check at boundaries (job inputs, JSON payloads) before S3 calls","Convert plain keys explicitly with S3Object(s3=key)","Validate dicts coming from older SDK versions or external systems","Reject None and local paths early with a clear error"],"tags":["s3","type-error","validation","python"],"backgroundTag":"invalid-argument-value","analyzedSha":"e474e8803ce2ff5c2df09a58dab51d45f5c922ca","analyzedAt":"2026-09-03T12:38:19.024Z","contentChangedAt":"2026-09-03T12:38:19.024Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}