# Buggy code
def resolve_anyref(self, refdoc, node, contnode):
    # type: (unicode, nodes.Node, nodes.Node) -> nodes.Node
    """Resolve reference generated by the "any" role."""
    stddomain = self.env.get_domain("std")
    target = node["reftarget"]
    results = []  # type: List[Tuple[unicode, nodes.Node]]
    # first, try resolving as :doc:
    doc_ref = stddomain.resolve_xref(
        self.env, refdoc, self.app.builder, "doc", target, node, contnode
    )
    if doc_ref:
        results.append(("doc", doc_ref))
    # next, do the standard domain (makes this a priority)
    results.extend(
        stddomain.resolve_any_xref(
            self.env, refdoc, self.app.builder, target, node, contnode
        )
    )
    for domain in self.env.domains.values():
        if domain.name == "std":
            continue  # we did this one already
        try:
            results.extend(
                domain.resolve_any_xref(
                    self.env, refdoc, self.app.builder, target, node, contnode
                )
            )
        except NotImplementedError:
            # the domain doesn't yet support the new interface
            # we have to manually collect possible references (SLOW)
            for role in domain.roles:
                res = domain.resolve_xref(
                    self.env, refdoc, self.app.builder, role, target, node, contnode
                )
                if res and isinstance(res[0], nodes.Element):
                    results.append(("%s:%s" % (domain.name, role), res))
    # now, see how many matches we got...
    if not results:
        return None
    if len(results) > 1:
        nice_results = " or ".join(
            ":%s:`%s`" % (name, role["reftitle"]) for name, role in results
        )
        logger.warning(
            __("more than one target found for 'any' cross-reference %r: could be %s"),
            target,
            nice_results,
            location=node,
        )
    res_role, newnode = results[0]
    # Override "any" class with the actual role type to get the styling
    # approximately correct.
    res_domain = res_role.split(":")[0]
    if newnode and newnode[0].get("classes"):
        newnode[0]["classes"].append(res_domain)
        newnode[0]["classes"].append(res_role.replace(":", "-"))
    return newnode


# Traceback (KeyError)
Traceback (most recent call last):
File "[...]/lib/python3.6/site-packages/sphinx/cmdline.py", line 304, in main
app.build(args.force_all, filenames)
File "[...]/lib/python3.6/site-packages/sphinx/application.py", line 330, in build
self.builder.build_update()
File "[...]/lib/python3.6/site-packages/sphinx/builders/__init__.py", line 333, in build_update
'out of date' % len(to_build))
File "[...]/lib/python3.6/site-packages/sphinx/builders/__init__.py", line 394, in build
self.write(docnames, list(updated_docnames), method)
File "[...]/lib/python3.6/site-packages/sphinx/builders/__init__.py", line 431, in write
self._write_serial(sorted(docnames))
File "[...]/lib/python3.6/site-packages/sphinx/builders/__init__.py", line 438, in _write_serial
doctree = self.env.get_and_resolve_doctree(docname, self)
File "[...]/lib/python3.6/site-packages/sphinx/environment/__init__.py", line 813, in get_and_resolve_doctree
self.apply_post_transforms(doctree, docname)
File "[...]/lib/python3.6/site-packages/sphinx/environment/__init__.py", line 860, in apply_post_transforms
transformer.apply_transforms()
File "[...]/lib/python3.6/site-packages/sphinx/transforms/__init__.py", line 96, in apply_transforms
Transformer.apply_transforms(self)
File "[...]/lib/python3.6/site-packages/docutils/transforms/__init__.py", line 171, in apply_transforms
transform.apply(**kwargs)
File "[...]/lib/python3.6/site-packages/sphinx/transforms/post_transforms/__init__.py", line 93, in apply
newnode = self.resolve_anyref(refdoc, node, contnode)
File "[...]/lib/python3.6/site-packages/sphinx/transforms/post_transforms/__init__.py", line 139, in resolve_anyref
for name, role in results)
File "[...]/lib/python3.6/site-packages/sphinx/transforms/post_transforms/__init__.py", line 139, in <genexpr>
for name, role in results)
File "[...]/lib/python3.6/site-packages/docutils/nodes.py", line 572, in __getitem__
raise e
File "[...]/lib/python3.6/site-packages/docutils/nodes.py", line 568, in __getitem__
return self.attributes[key]
KeyError: 'reftitle'

# Fixed code
def resolve_anyref(self, refdoc, node, contnode):
    # type: (unicode, nodes.Node, nodes.Node) -> nodes.Node
    """Resolve reference generated by the "any" role."""
    stddomain = self.env.get_domain("std")
    target = node["reftarget"]
    results = []  # type: List[Tuple[unicode, nodes.Node]]
    # first, try resolving as :doc:
    doc_ref = stddomain.resolve_xref(
        self.env, refdoc, self.app.builder, "doc", target, node, contnode
    )
    if doc_ref:
        results.append(("doc", doc_ref))
    # next, do the standard domain (makes this a priority)
    results.extend(
        stddomain.resolve_any_xref(
            self.env, refdoc, self.app.builder, target, node, contnode
        )
    )
    for domain in self.env.domains.values():
        if domain.name == "std":
            continue  # we did this one already
        try:
            results.extend(
                domain.resolve_any_xref(
                    self.env, refdoc, self.app.builder, target, node, contnode
                )
            )
        except NotImplementedError:
            # the domain doesn't yet support the new interface
            # we have to manually collect possible references (SLOW)
            for role in domain.roles:
                res = domain.resolve_xref(
                    self.env, refdoc, self.app.builder, role, target, node, contnode
                )
                if res and isinstance(res[0], nodes.Element):
                    results.append(("%s:%s" % (domain.name, role), res))
    # now, see how many matches we got...
    if not results:
        return None
    if len(results) > 1:

        def stringify(name, node):
            reftitle = node.get("reftitle", node.astext())
            return ":%s:`%s`" % (name, reftitle)

        candidates = " or ".join(stringify(name, role) for name, role in results)
        logger.warning(
            __("more than one target found for 'any' cross-reference %r: could be %s"),
            target,
            candidates,
            location=node,
        )
    res_role, newnode = results[0]
    # Override "any" class with the actual role type to get the styling
    # approximately correct.
    res_domain = res_role.split(":")[0]
    if newnode and newnode[0].get("classes"):
        newnode[0]["classes"].append(res_domain)
        newnode[0]["classes"].append(res_role.replace(":", "-"))
    return newnode