# Buggy code
def get_url_parts(self, request=None):
    """
    Determine the URL for this page and return it as a tuple of
    ``(site_id, site_root_url, page_url_relative_to_site_root)``.
    Return None if the page is not routable.

    This is used internally by the ``full_url``, ``url``, ``relative_url``
    and ``get_site`` properties and methods; pages with custom URL routing
    should override this method in order to have those operations return
    the custom URLs.

    Accepts an optional keyword argument ``request``, which may be used
    to avoid repeated database / cache lookups. Typically, a page model
    that overrides ``get_url_parts`` should not need to deal with
    ``request`` directly, and should just pass it to the original method
    when calling ``super``.
    """

    possible_sites = [
        (pk, path, url, language_code)
        for pk, path, url, language_code in self._get_site_root_paths(request)
        if self.url_path.startswith(path)
    ]

    if not possible_sites:
        return None

    site_id, root_path, root_url, language_code = possible_sites[0]

    site = Site.find_for_request(request)
    if site:
        for site_id, root_path, root_url, language_code in possible_sites:
            if site_id == site.pk:
                break
        else:
            site_id, root_path, root_url, language_code = possible_sites[0]

    # If the active language code is a variant of the page's language, then
    # use that instead
    # This is used when LANGUAGES contain more languages than WAGTAIL_CONTENT_LANGUAGES
    if (
        get_supported_content_language_variant(translation.get_language())
        == language_code
    ):
        language_code = translation.get_language()

    # The page may not be routable because wagtail_serve is not registered
    # This may be the case if Wagtail is used headless
    try:
        with translation.override(language_code):
            page_path = reverse(
                "wagtail_serve", args=(self.url_path[len(root_path) :],)
            )
    except NoReverseMatch:
        return (site_id, None, None)

    # Remove the trailing slash from the URL reverse generates if
    # WAGTAIL_APPEND_SLASH is False and we're not trying to serve
    # the root path
    if not WAGTAIL_APPEND_SLASH and page_path != "/":
        page_path = page_path.rstrip("/")

    return (site_id, root_url, page_path)


# Traceback (LookupError)
Internal Server Error: /admin/api/main/pages/
Traceback (most recent call last):
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/django/core/handlers/base.py", line 179, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
response = view_func(request, *args, **kwargs)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/wagtail/admin/urls/__init__.py", line 127, in wrapper
return view_func(request, *args, **kwargs)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/wagtail/admin/auth.py", line 170, in decorated_view
response = view_func(request, *args, **kwargs)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/wagtail/api/v2/router.py", line 63, in wrapped
return func(request, *args, **kwargs)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/rest_framework/viewsets.py", line 125, in view
return self.dispatch(request, *args, **kwargs)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/rest_framework/views.py", line 509, in dispatch
response = self.handle_exception(exc)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/wagtail/api/v2/views.py", line 116, in handle_exception
return super().handle_exception(exc)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/rest_framework/views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
raise exc
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/rest_framework/views.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/wagtail/admin/api/views.py", line 98, in listing_view
response = super().listing_view(request)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/wagtail/api/v2/views.py", line 74, in listing_view
return self.get_paginated_response(serializer.data)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/rest_framework/serializers.py", line 745, in data
ret = super().data
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/rest_framework/serializers.py", line 246, in data
self._data = self.to_representation(self.instance)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/rest_framework/serializers.py", line 663, in to_representation
return [
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/rest_framework/serializers.py", line 664, in <listcomp>
self.child.to_representation(item) for item in iterable
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/wagtail/api/v2/serializers.py", line 287, in to_representation
meta[field.field_name] = field.to_representation(attribute)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/wagtail/api/v2/serializers.py", line 62, in to_representation
return page.full_url
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/wagtail/core/models.py", line 1609, in get_full_url
url_parts = self.get_url_parts(request=request)
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/wagtail/core/models.py", line 1587, in get_url_parts
if get_supported_content_language_variant(translation.get_language()) == language_code:
File "/Users/matthew/.virtualenvs/langwithoutwagtail/lib/python3.9/site-packages/wagtail/core/utils.py", line 271, in get_supported_content_language_variant
raise LookupError(lang_code)
LookupError: fr

# Fixed code
def get_url_parts(self, request=None):
    """
    Determine the URL for this page and return it as a tuple of
    ``(site_id, site_root_url, page_url_relative_to_site_root)``.
    Return None if the page is not routable.

    This is used internally by the ``full_url``, ``url``, ``relative_url``
    and ``get_site`` properties and methods; pages with custom URL routing
    should override this method in order to have those operations return
    the custom URLs.

    Accepts an optional keyword argument ``request``, which may be used
    to avoid repeated database / cache lookups. Typically, a page model
    that overrides ``get_url_parts`` should not need to deal with
    ``request`` directly, and should just pass it to the original method
    when calling ``super``.
    """

    possible_sites = [
        (pk, path, url, language_code)
        for pk, path, url, language_code in self._get_site_root_paths(request)
        if self.url_path.startswith(path)
    ]

    if not possible_sites:
        return None

    site_id, root_path, root_url, language_code = possible_sites[0]

    site = Site.find_for_request(request)
    if site:
        for site_id, root_path, root_url, language_code in possible_sites:
            if site_id == site.pk:
                break
        else:
            site_id, root_path, root_url, language_code = possible_sites[0]

    use_wagtail_i18n = getattr(settings, "WAGTAIL_I18N_ENABLED", False)

    if use_wagtail_i18n:
        # If the active language code is a variant of the page's language, then
        # use that instead
        # This is used when LANGUAGES contain more languages than WAGTAIL_CONTENT_LANGUAGES
        try:
            if (
                get_supported_content_language_variant(translation.get_language())
                == language_code
            ):
                language_code = translation.get_language()
        except LookupError:
            # active language code is not a recognised content language, so leave
            # page's language code unchanged
            pass

    # The page may not be routable because wagtail_serve is not registered
    # This may be the case if Wagtail is used headless
    try:
        if use_wagtail_i18n:
            with translation.override(language_code):
                page_path = reverse(
                    "wagtail_serve", args=(self.url_path[len(root_path) :],)
                )
        else:
            page_path = reverse(
                "wagtail_serve", args=(self.url_path[len(root_path) :],)
            )
    except NoReverseMatch:
        return (site_id, None, None)

    # Remove the trailing slash from the URL reverse generates if
    # WAGTAIL_APPEND_SLASH is False and we're not trying to serve
    # the root path
    if not WAGTAIL_APPEND_SLASH and page_path != "/":
        page_path = page_path.rstrip("/")

    return (site_id, root_url, page_path)