Skip to content

derivepassphrase bug better-error-messages

Bug details: Improve common error messages in the command-line interface

ClassbugThis is clearly an actual problem we want fixed.
Present-in0.1.0 0.1.1 0.1.2 0.1.3
Fixed-ine662c2e71c50e57f465fdeb8efb403ed77147e8c (0.2.0)

Except for those related to command-line parsing, all error messages in derivepassphrase are written to be useful to API users, not command-line end users. Currently, the command-line interface passes those errors through without modification, either via default behavior of click, or by explicitly via ctx.fail. At the same time, the API uses standard error types directly if possible, and omits context that is already encoded in the error type. But this context is no longer available on the command-line because only the error message (and not the type) is relayed.

Example (trying to set up SSH key use with derivepassphrase)
$ env -u SSH_AUTH_SOCK derivepassphrase -k --config
Usage: derivepassphrase [OPTIONS] [SERVICE]
Try 'derivepassphrase -h' for help.

Error: 'SSH_AUTH_SOCK environment variable'
What's actually happening:
>>> import os
>>> del os.environ['SSH_AUTH_SOCK']
>>> from derivepassphrase.cli import derivepassphrase as main
>>> main.main(args=['-k', '--config'], standalone_mode=False)
Traceback (most recent call last):
  File ".../derivepassphrase/cli.py", line 996, in derivepassphrase
    key = base64.standard_b64encode(_select_ssh_key()).decode(
                                    ^^^^^^^^^^^^^^^^^
  File ".../derivepassphrase/cli.py", line 297, in _select_ssh_key
    suitable_keys = list(_get_suitable_ssh_keys(conn))
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../derivepassphrase/cli.py", line 169, in _get_suitable_ssh_keys
    client = ssh_agent_client.SSHAgentClient(socket=conn)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../ssh_agent_client/__init__.py", line 95, in __init__
    raise KeyError(msg) from None
KeyError: 'SSH_AUTH_SOCK environment variable'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File ".../click/core.py", line 1078, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File ".../click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../click/decorators.py", line 33, in new_func
    return f(get_current_context(), *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File ".../derivepassphrase/cli.py", line 1002, in derivepassphrase
    ctx.fail(str(e))
  File ".../click/core.py", line 684, in fail
    raise UsageError(message, self)
click.exceptions.UsageError: 'SSH_AUTH_SOCK environment variable'

I believe the solution is two-fold. First, the API should use proper error subtypes, even if they partially duplicate the error message. Second, the command-line interface should guard all calls into the derivepassphrase machinery with proper error checking, and emit more suitable error messages if necessary.