# Buggy code
def start_onion_service(self, port):
    """
    Start a onion service on port 80, pointing to the given port, and
    return the onion hostname.
    """
    self.common.log("Onion", "start_onion_service")

    self.auth_string = None
    if not self.supports_ephemeral:
        raise TorTooOld(strings._("error_ephemeral_not_supported"))
    if self.stealth and not self.supports_stealth:
        raise TorTooOld(strings._("error_stealth_not_supported"))

    print(strings._("config_onion_service").format(int(port)))
    print(strings._("using_ephemeral"))

    if self.stealth:
        if self.settings.get("hidservauth_string"):
            hidservauth_string = self.settings.get("hidservauth_string").split()[2]
            basic_auth = {"onionshare": hidservauth_string}
        else:
            basic_auth = {"onionshare": None}
    else:
        basic_auth = None

    if self.settings.get("private_key"):
        key_content = self.settings.get("private_key")
        # is the key a v2 key?
        if onionkey.is_v2_key(key_content):
            key_type = "RSA1024"
        # The below section is commented out because re-publishing
        # a pre-prepared v3 private key is currently unstable in Tor.
        # This is fixed upstream but won't reach stable until 0.3.5
        # (expected in December 2018)
        # See https://trac.torproject.org/projects/tor/ticket/25552
        # Until then, we will deliberately not work with 'persistent'
        # v3 onions, which should not be possible via the GUI settings
        # anyway.
        # Our ticket: https://github.com/micahflee/onionshare/issues/677
        #
        # Assume it was a v3 key
        # key_type = "ED25519-V3"
        else:
            raise TorErrorProtocolError(strings._("error_invalid_private_key"))
        self.common.log("Onion", "Starting a hidden service with a saved private key")
    else:
        # Work out if we can support v3 onion services, which are preferred
        if Version(self.tor_version) >= Version("0.3.2.9") and not self.settings.get(
            "use_legacy_v2_onions"
        ):
            key_type = "ED25519-V3"
            key_content = onionkey.generate_v3_private_key()[0]
        else:
            # fall back to v2 onion services
            key_type = "RSA1024"
            key_content = onionkey.generate_v2_private_key()[0]
        self.common.log("Onion", "Starting a hidden service with a new private key")

    # v3 onions don't yet support basic auth. Our ticket:
    # https://github.com/micahflee/onionshare/issues/697
    if key_type == "ED25519-V3" and not self.settings.get("use_legacy_v2_onions"):
        basic_auth = None
        self.stealth = False

    try:
        if basic_auth != None:
            res = self.c.create_ephemeral_hidden_service(
                {80: port},
                await_publication=True,
                basic_auth=basic_auth,
                key_type=key_type,
                key_content=key_content,
            )
        else:
            # if the stem interface is older than 1.5.0, basic_auth isn't a valid keyword arg
            res = self.c.create_ephemeral_hidden_service(
                {80: port},
                await_publication=True,
                key_type=key_type,
                key_content=key_content,
            )

    except ProtocolError:
        raise TorErrorProtocolError(strings._("error_tor_protocol_error"))

    self.service_id = res.service_id
    onion_host = self.service_id + ".onion"

    # A new private key was generated and is in the Control port response.
    if self.settings.get("save_private_key"):
        if not self.settings.get("private_key"):
            self.settings.set("private_key", key_content)

    if self.stealth:
        # Similar to the PrivateKey, the Control port only returns the ClientAuth
        # in the response if it was responsible for creating the basic_auth password
        # in the first place.
        # If we sent the basic_auth (due to a saved hidservauth_string in the settings),
        # there is no response here, so use the saved value from settings.
        if self.settings.get("save_private_key"):
            if self.settings.get("hidservauth_string"):
                self.auth_string = self.settings.get("hidservauth_string")
            else:
                auth_cookie = list(res.client_auth.values())[0]
                self.auth_string = "HidServAuth {} {}".format(onion_host, auth_cookie)
                self.settings.set("hidservauth_string", self.auth_string)
        else:
            auth_cookie = list(res.client_auth.values())[0]
            self.auth_string = "HidServAuth {} {}".format(onion_host, auth_cookie)

    if onion_host is not None:
        self.settings.save()
        return onion_host
    else:
        raise TorErrorProtocolError(strings._("error_tor_protocol_error"))


# Traceback (stem.ProtocolError)
$ ./dev_scripts/onionshare --receive
OnionShare 1.3.1 | https://onionshare.org/
Connecting to the Tor network: 100% - Done
Configuring onion service on port 17641.
Starting ephemeral Tor onion service and awaiting publication
Traceback (most recent call last):
File "/home/user/code/onionshare/onionshare/onion.py", line 483, in start_onion_service
res = self.c.create_ephemeral_hidden_service({ 80: port }, await_publication=True, key_type=key_type, key_content=key_content)
File "/usr/lib/python3.6/site-packages/stem/control.py", line 2937, in create_ephemeral_hidden_service
stem.response.convert('ADD_ONION', response)
File "/usr/lib/python3.6/site-packages/stem/response/__init__.py", line 124, in convert
message._parse_message(**kwargs)
File "/usr/lib/python3.6/site-packages/stem/response/add_onion.py", line 31, in _parse_message
raise stem.ProtocolError("ADD_ONION response didn't have an OK status: %s" % self)
stem.ProtocolError: ADD_ONION response didn't have an OK status: Invalid key type

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "./dev_scripts/onionshare", line 28, in <module>
onionshare.main()
File "/home/user/code/onionshare/onionshare/__init__.py", line 110, in main
app.start_onion_service()
File "/home/user/code/onionshare/onionshare/onionshare.py", line 86, in start_onion_service
self.onion_host = self.onion.start_onion_service(self.port)
File "/home/user/code/onionshare/onionshare/onion.py", line 486, in start_onion_service
raise TorErrorProtocolError(strings._('error_tor_protocol_error'))
onionshare.onion.TorErrorProtocolError: Could not communicate with the Tor controller.
If you're using Whonix, check out https://www.whonix.org/wiki/onionshare to make OnionShare work.

# Fixed code
def start_onion_service(self, port):
    """
    Start a onion service on port 80, pointing to the given port, and
    return the onion hostname.
    """
    self.common.log("Onion", "start_onion_service")

    self.auth_string = None
    if not self.supports_ephemeral:
        raise TorTooOld(strings._("error_ephemeral_not_supported"))
    if self.stealth and not self.supports_stealth:
        raise TorTooOld(strings._("error_stealth_not_supported"))

    print(strings._("config_onion_service").format(int(port)))
    print(strings._("using_ephemeral"))

    if self.stealth:
        if self.settings.get("hidservauth_string"):
            hidservauth_string = self.settings.get("hidservauth_string").split()[2]
            basic_auth = {"onionshare": hidservauth_string}
        else:
            basic_auth = {"onionshare": None}
    else:
        basic_auth = None

    if self.settings.get("private_key"):
        key_content = self.settings.get("private_key")
        # is the key a v2 key?
        if onionkey.is_v2_key(key_content):
            key_type = "RSA1024"
            # The below section is commented out because re-publishing
            # a pre-prepared v3 private key is currently unstable in Tor.
            # This is fixed upstream but won't reach stable until 0.3.5
            # (expected in December 2018)
            # See https://trac.torproject.org/projects/tor/ticket/25552
            # Until then, we will deliberately not work with 'persistent'
            # v3 onions, which should not be possible via the GUI settings
            # anyway.
            # Our ticket: https://github.com/micahflee/onionshare/issues/677
            #
            # Assume it was a v3 key
            # key_type = "ED25519-V3"
        else:
            raise TorErrorProtocolError(strings._("error_invalid_private_key"))
    else:
        # Work out if we can support v3 onion services, which are preferred
        if Version(self.tor_version) >= Version("0.3.3.1") and not self.settings.get(
            "use_legacy_v2_onions"
        ):
            key_type = "ED25519-V3"
            key_content = onionkey.generate_v3_private_key()[0]
        else:
            # fall back to v2 onion services
            key_type = "RSA1024"
            key_content = onionkey.generate_v2_private_key()[0]

    # v3 onions don't yet support basic auth. Our ticket:
    # https://github.com/micahflee/onionshare/issues/697
    if key_type == "ED25519-V3" and not self.settings.get("use_legacy_v2_onions"):
        basic_auth = None
        self.stealth = False

    self.common.log("Onion", "start_onion_service", "key_type={}".format(key_type))
    try:
        if basic_auth != None:
            res = self.c.create_ephemeral_hidden_service(
                {80: port},
                await_publication=True,
                basic_auth=basic_auth,
                key_type=key_type,
                key_content=key_content,
            )
        else:
            # if the stem interface is older than 1.5.0, basic_auth isn't a valid keyword arg
            res = self.c.create_ephemeral_hidden_service(
                {80: port},
                await_publication=True,
                key_type=key_type,
                key_content=key_content,
            )

    except ProtocolError as e:
        raise TorErrorProtocolError(
            strings._("error_tor_protocol_error").format(e.args[0])
        )

    self.service_id = res.service_id
    onion_host = self.service_id + ".onion"

    # A new private key was generated and is in the Control port response.
    if self.settings.get("save_private_key"):
        if not self.settings.get("private_key"):
            self.settings.set("private_key", key_content)

    if self.stealth:
        # Similar to the PrivateKey, the Control port only returns the ClientAuth
        # in the response if it was responsible for creating the basic_auth password
        # in the first place.
        # If we sent the basic_auth (due to a saved hidservauth_string in the settings),
        # there is no response here, so use the saved value from settings.
        if self.settings.get("save_private_key"):
            if self.settings.get("hidservauth_string"):
                self.auth_string = self.settings.get("hidservauth_string")
            else:
                auth_cookie = list(res.client_auth.values())[0]
                self.auth_string = "HidServAuth {} {}".format(onion_host, auth_cookie)
                self.settings.set("hidservauth_string", self.auth_string)
        else:
            auth_cookie = list(res.client_auth.values())[0]
            self.auth_string = "HidServAuth {} {}".format(onion_host, auth_cookie)

    if onion_host is not None:
        self.settings.save()
        return onion_host
    else:
        raise TorErrorProtocolError(strings._("error_tor_protocol_error_unknown"))