dvc icon indicating copy to clipboard operation
dvc copied to clipboard

s3 remote: handle invalid auth information properly

Open shcheklein opened this issue 4 years ago • 5 comments

Bug Report

If S3 auth token is invalid, DVC throws unexpected error

Description

I have MFA (2FA) setup. When temporary token is expired, and I try to push/pull, I'm getting an unexpected error from DVC:

ERROR: unexpected error - [Errno 22] The provided token is malformed or otherwise invalid.: An error occurred (InvalidToken) when calling the ListObjectsV2 operation: The provided token is malformed or otherwise invalid.

Quick fix is to update the token with AWS STS tools.

Reproduce

Use MFA Wait token expire Try to push something to S3

Expected

It's not an unexpected error. It should be handled properly with some useful message on what is wrong. The provided token is malformed or otherwise invalid. is not useful.

Environment information

Output of dvc doctor:

DVC version: 2.5.4+6c8673
---------------------------------
Platform: Python 3.8.9 on macOS-10.15.6-x86_64-i386-64bit
Supports:
	azure (adlfs = 2021.7.0, knack = 0.8.2, azure-identity = 1.5.0),
	gdrive (pydrive2 = 1.8.2),
	gs (gcsfs = 2021.7.0),
	hdfs (pyarrow = 4.0.0),
	webhdfs (hdfs = 2.5.8),
	http (requests = 2.25.1),
	https (requests = 2.25.1),
	s3 (s3fs = 2021.7.0, boto3 = 1.17.49),
	ssh (paramiko = 2.7.2),
	oss (ossfs = 2021.7.3),
	webdav (webdav4 = 0.8.2),
	webdavs (webdav4 = 0.8.2)
Cache types: reflink, hardlink, symlink
Cache directory: apfs on /dev/disk1s1
Caches: local
Remotes: s3
Workspace directory: apfs on /dev/disk1s1
Repo: dvc, git
Stack trace with `-v`
2021-07-15 08:34:10,784 ERROR: unexpected error - [Errno 22] The provided token is malformed or otherwise invalid.: An error occurred (InvalidToken) when calling the ListObjectsV2 operation: The provided token is malformed or otherwise invalid.
------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/ivan/Projects/example-get-started/.env/lib/python3.8/site-packages/s3fs/core.py", line 246, in _call_s3
    out = await method(**additional_kwargs)
  File "/Users/ivan/Projects/example-get-started/.env/lib/python3.8/site-packages/aiobotocore/client.py", line 154, in _make_api_call
    raise error_class(parsed_response, operation_name)
botocore.exceptions.ClientError: An error occurred (InvalidToken) when calling the ListObjectsV2 operation: The provided token is malformed or otherwise invalid.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/Users/ivan/Projects/dvc/dvc/main.py", line 55, in main
    ret = cmd.do_run()
  File "/Users/ivan/Projects/dvc/dvc/command/base.py", line 50, in do_run
    return self.run()
  File "/Users/ivan/Projects/dvc/dvc/command/data_sync.py", line 57, in run
    processed_files_count = self.repo.push(
  File "/Users/ivan/Projects/dvc/dvc/repo/__init__.py", line 51, in wrapper
    return f(repo, *args, **kwargs)
  File "/Users/ivan/Projects/dvc/dvc/repo/push.py", line 44, in push
    pushed += self.cloud.push(objs, jobs, remote=remote)
  File "/Users/ivan/Projects/dvc/dvc/data_cloud.py", line 79, in push
    return remote_obj.push(
  File "/Users/ivan/Projects/dvc/dvc/remote/base.py", line 57, in wrapper
    return f(obj, *args, **kwargs)
  File "/Users/ivan/Projects/dvc/dvc/remote/base.py", line 488, in push
    ret = self._process(
  File "/Users/ivan/Projects/dvc/dvc/remote/base.py", line 345, in _process
    dir_status, file_status, dir_contents = self._status(
  File "/Users/ivan/Projects/dvc/dvc/remote/base.py", line 189, in _status
    remote_exists.update(self._indexed_dir_hashes(dir_objs))
  File "/Users/ivan/Projects/dvc/dvc/remote/base.py", line 252, in _indexed_dir_hashes
    self.odb.list_hashes_exists(indexed_dirs)
  File "/Users/ivan/Projects/dvc/dvc/objects/db/base.py", line 419, in list_hashes_exists
    ret = list(itertools.compress(hashes, in_remote))
  File "/usr/local/opt/[email protected]/Frameworks/Python.framework/Versions/3.8/lib/python3.8/concurrent/futures/_base.py", line 619, in result_iterator
    yield fs.pop().result()
  File "/usr/local/opt/[email protected]/Frameworks/Python.framework/Versions/3.8/lib/python3.8/concurrent/futures/_base.py", line 444, in result
    return self.__get_result()
  File "/usr/local/opt/[email protected]/Frameworks/Python.framework/Versions/3.8/lib/python3.8/concurrent/futures/_base.py", line 389, in __get_result
    raise self._exception
  File "/usr/local/opt/[email protected]/Frameworks/Python.framework/Versions/3.8/lib/python3.8/concurrent/futures/thread.py", line 57, in run
    result = self.fn(*self.args, **self.kwargs)
  File "/Users/ivan/Projects/dvc/dvc/objects/db/base.py", line 410, in exists_with_progress
    ret = self.fs.exists(path_info)
  File "/Users/ivan/Projects/dvc/dvc/fs/fsspec_wrapper.py", line 83, in exists
    return self.fs.exists(self._with_bucket(path_info))
  File "/Users/ivan/Projects/example-get-started/.env/lib/python3.8/site-packages/fsspec/asyn.py", line 88, in wrapper
    return sync(self.loop, func, *args, **kwargs)
  File "/Users/ivan/Projects/example-get-started/.env/lib/python3.8/site-packages/fsspec/asyn.py", line 69, in sync
    raise result[0]
  File "/Users/ivan/Projects/example-get-started/.env/lib/python3.8/site-packages/fsspec/asyn.py", line 25, in _runner
    result[0] = await coro
  File "/Users/ivan/Projects/example-get-started/.env/lib/python3.8/site-packages/s3fs/core.py", line 804, in _exists
    await self._info(path, bucket, key, version_id=version_id)
  File "/Users/ivan/Projects/example-get-started/.env/lib/python3.8/site-packages/s3fs/core.py", line 1064, in _info
    out = await self._simple_info(path)
  File "/Users/ivan/Projects/example-get-started/.env/lib/python3.8/site-packages/s3fs/core.py", line 977, in _simple_info
    out = await self._call_s3(
  File "/Users/ivan/Projects/example-get-started/.env/lib/python3.8/site-packages/s3fs/core.py", line 265, in _call_s3
    raise translate_boto_error(err)
OSError: [Errno 22] The provided token is malformed or otherwise invalid.

shcheklein avatar Jul 15 '21 15:07 shcheklein

The provided token is malformed or otherwise invalid. is not useful.

@shcheklein Could you elaborate on why it is not useful, please? It precisely tells you what's wrong and that error is searchable. Just wondering what kind of error you expect here.

efiop avatar Jul 15 '21 16:07 efiop

The provided token is malformed or otherwise invalid.

To be precise - this is a second-order issue. First issue here is that ERROR is unexpected while it's not.

Now, why do I think it's not useful, since it's not clear what token it is. At least modifying it to MFA (2FA) token, or use the same terminology as AWS has - "session token". We need something that would connect me to the actual sub-system, part of the auth flow that is failing.

It would be even better to say something - "Looks like your MFA (2FA) session token is expired or invalid"

shcheklein avatar Jul 15 '21 16:07 shcheklein

To be precise - this is a second-order issue. First issue here is that ERROR is unexpected while it's not.

Agreed, this is a generally well-known issue with non-dvc exceptions. Def could reconsider among other things.

It would be even better to say something - "Looks like your MFA (2FA) session token is expired or invalid"

It pretty much says the same thing as The provided token is malformed or otherwise invalid., but is no longer searchable on google, so you'll likely get less good explanations and instructions. Though we could just include a link for https://dvc.org/doc/user-guide/troubleshooting#troubleshooting , where we could give a more useful verbose explanation and recipe on how to fix it and freely play with wording and stuff. A link with a very brief message like Authentication error (see link-to-troubleshooting) (instead of rephrasing in our code base like in your suggestion) is probably best and the easiest to maintain. But need to take a closer look to say for sure.

efiop avatar Jul 15 '21 16:07 efiop

Agreed, this is a generally well-known issue with non-dvc exceptions. Def could reconsider among other things.

Yep, to me it looks like a bug I think. Unexpected error to me means that something actually critical error (bug) happened.

Though we could just include a link for https://dvc.org/doc/user-guide/troubleshooting#troubleshooting

Good point. Do one generic "Auth failed " vs being more specific in the message for me that would depend on how bad those libraries that we use. If it's hard to get that information, then generic message with a link is enough I think.

instead of rephrasing in our code base like in your suggestion

It's not what I was suggesting to be precise :) I was only stating that it's not a very good message from the user perspective and can be improved. How? Ideally in the libraries that we use if they take that part of auth, to the extent that is possible. I also don't like parsing exception texts with regexps and wrapping them up. I would though go even that path if it's needed (depends on the availability of other options, etc, etc).

shcheklein avatar Jul 15 '21 17:07 shcheklein

I also felt like we could have better error messages.

$ dvc checkout   
WARNING: No file hash info found for '../../iterative.ai/static/uploads'. It won't be created.                                                            
ERROR: Checkout failed for following targets:                                                                                                             ../../iterative.ai/static/uploads
Is your cache up to date?
<https://error.dvc.org/missing-files>

It's again due to the AWS MFA session token expired and couldn't checkout. Also on dvc pull: image

It would be better if these messages had some hint of authentication error. I don't see any on both of them.

yathomasi avatar Sep 15 '22 16:09 yathomasi