Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AttributeError at /api/v1/timelines/public 'NoneType' object has no attribute 'replace' #610

Open
doodlemania2 opened this issue Jul 17, 2023 · 18 comments

Comments

@doodlemania2
Copy link

doodlemania2 commented Jul 17, 2023

Got about 70 of these over the course of an hour this morning.
Let me know if you need more of the dump - trying to be careful with the securestrings

Partial dump:

Request Method: GET
Request URL: http://url2836.thedoodleproject.net/ls/click?upn=E-2BIWnGoNaV1xc2v5aE-2B4Gzx6BY1Kbt5iQDnHCgHyWoX1QPkgRbuoixoHLDdTkMS2nLs45NGjO8Eo5LlRGdCm6Ym3uOJr3UJa-2FtSZ1CQP7-2Bk-3Dz8aE_MTQKioXqYBjSf6sZsbopRxXOuEG0ZOkr5NI5G2u0JGvp4JLCMa-2FFTbuuKL-2FB4dNYxIfTBSHCYd8P-2BEaAIfLCpYDIEXVDzTXlsE9-2BfudeAHeaLnsEvE79YMzUy3Nq9AA-2FvSVWpa-2FuBxB0ewglfrgLo98PRJT9OaKFg8eTmDQIWiPHRWLNJsYVlD6k6-2BQLhkrjdlQiEH9oNXI-2BjuGf26g00IxEmMLdnX4obkNu6e1o41Q-3D
Django Version: 4.2.2
Python Executable: /usr/local/bin/python3
Python Version: 3.11.4
Python Path: ['/takahe', '/usr/local/bin', '/usr/local/lib/python311.zip', '/usr/local/lib/python3.11', '/usr/local/lib/python3.11/lib-dynload', '/usr/local/lib/python3.11/site-packages']
Server time: Mon, 17 Jul 2023 09:27:44 +0000
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.postgres',
'corsheaders',
'django_htmx',
'hatchway',
'core',
'activities',
'api',
'mediaproxy',
'stator',
'users']
Installed Middleware:
['core.middleware.SentryTaggingMiddleware',
'django.middleware.security.SecurityMiddleware',
'corsheaders.middleware.CorsMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django_htmx.middleware.HtmxMiddleware',
'core.middleware.HeadersMiddleware',
'core.middleware.ConfigLoadingMiddleware',
'api.middleware.ApiTokenMiddleware',
'users.middleware.DomainMiddleware']

Traceback (most recent call last):
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/site-packages/hatchway/view.py", line 302, in call
response = self.view(request, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/takahe/api/views/timelines.py", line 84, in public
schemas.Status.map_from_post(pager.results, request.identity),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/takahe/api/schemas.py", line 189, in map_from_post
return [

File "/takahe/api/schemas.py", line 190, in
cls.from_post(
^
File "/takahe/api/schemas.py", line 172, in from_post
**post.to_mastodon_json(

File "/takahe/activities/models/post.py", line 1113, in to_mastodon_json
"content": self.safe_content_remote(),
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/takahe/activities/models/post.py", line 430, in safe_content_remote
return self.safe_content(local=False)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/takahe/activities/models/post.py", line 417, in safe_content
return func(local=local)
^^^^^^^^^^^^^^^^^
File "/takahe/activities/models/post.py", line 385, in _safe_content_note
return ContentRenderer(local=local).render_post(self.content, self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/takahe/core/html.py", line 338, in render_post
parser = FediverseHtmlParser(

File "/takahe/core/html.py", line 81, in init
self.feed(html.replace("\n", ""))
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/html/parser.py", line 110, in feed
self.goahead(0)
^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/html/parser.py", line 172, in goahead
k = self.parse_endtag(i)
^^^^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/html/parser.py", line 413, in parse_endtag
self.handle_endtag(elem)
^^^^^^^^^^^^^^^^^^^^^^^^
File "/takahe/core/html.py", line 138, in handle_endtag
self.html_output += self.create_link(

File "/takahe/core/html.py", line 178, in create_link
return f'{html.escape(content)}'
^^^^^^^^^^^^^^^^^
File "/usr/local/lib/python3.11/html/init.py", line 19, in escape
s = s.replace("&", "&") # Must be done first!
^^^^^^^^^

Exception Type: AttributeError at /api/v1/timelines/public
Exception Value: 'NoneType' object has no attribute 'replace'
Raised during: hatchway.view.ApiView
Request information:
USER: AnonymousUser

GET:
limit = '40'

POST: No POST data

FILES: No FILES data

COOKIES: No cookie data

@andrewgodwin
Copy link
Member

Looks like you somehow have a post with no content. Let me add a good escape handler for that.

@andrewgodwin
Copy link
Member

andrewgodwin commented Jul 17, 2023

Hmm, what version of the code are you running? That traceback matches neither 0.9.0 or main, both of which seem to have defences against this.

@doodlemania2
Copy link
Author

Well that’s odd! I’m on 0.9.0…should I grab a fuller dump or re-pull?

@andrewgodwin
Copy link
Member

Are you able to get into your system and see what line 178 of core/html.py says for me? That's mostly what I need to know.

@doodlemania2
Copy link
Author

Hrm, so the webserver and runner server are both running jointakahe/takahe:latest. The containers have been nicely "minified" so vi, vim, nano aren't present, so not sure how to get to the exact line we need. Did a cat core/html.py | more but lost count. Is there a magical pipe command to grab the line requested from a cat command?

@andrewgodwin
Copy link
Member

You can just use | head -n 180 | tail -n 4 to get a segment in the right area.

Also busybox is in there, so I believe busybox vi should give you a very basic vi implementation.

@doodlemania2
Copy link
Author

slick!

root@takahe-serve:/takahe/core# cat html.py | head -n 180 | tail -n 4
else:
return f'{html.escape(content)}'

def create_mention(self, handle, href: str | None = None) -> str:

root@takahe-serve:/takahe/core#

@andrewgodwin
Copy link
Member

Well I don't know what version you have but it definitely isn't the latest image and I cannot for the life of me figure out what version it would be. What does your /takahe/takahe/__init__.py say the version is?

@doodlemania2
Copy link
Author

root@takahe-serve:/takahe/takahe# cat init.py
version = "0.9.0"
root@takahe-serve:/takahe/takahe#

Should I try to re-pull the images? This is my image from docker: sha256:f28d55e609a9288e18914523c38c7207c5bab169a8f93c2b819a315dc927702a

@humrochagf
Copy link
Contributor

Are you pulling it from docker jointakahe/takahe ?
0.9.0 / latest images have different signatures:
amd64 - sha256:adf569884c8acd9b8ecfbd0fe66816af3e800dd849e4d7a08fc040d4790ae004
arm64 - sha256:761b76f7350038256cbb0f25c351bca2aa2c2dd54383b73c2351c13bd7cc91e3

@doodlemania2
Copy link
Author

Yes, here's what Portainer says:
image

@doodlemania2
Copy link
Author

let me try a new "pull" just for giggles (shrug)

@doodlemania2
Copy link
Author

:/
says it is current
not sure what to do - weird that I appear to be running the not current'est version

@andrewgodwin
Copy link
Member

Yeah, I don't know what's happening here, but it's definitely not pulling actual latest. I'd suggest switching to explicit version tags (so 0.9.0 here) as a lot of Docker implementations do a lot of weird caching so that dynamic tags are pretty unpredictable to use.

@doodlemania2
Copy link
Author

Of course!
Now running explicit:
image

with SHAs
sha256:f28d55e609a9288e18914523c38c7207c5bab169a8f93c2b819a315dc927702a
same sha, but docker hub differs. what the heck :(

@andrewgodwin
Copy link
Member

Well that is incredibly strange. I also pulled the image to my local system and got a different SHA, even when pulling by SHA itself:

$ docker inspect --format='{{index .RepoDigests 0}}' jointakahe/takahe@sha256:adf569884c8acd9b8ecfbd0fe66816af3e800dd849e4d7a08fc040d4790ae004

jointakahe/takahe@sha256:62b8d50c43ab514b94bf403291b0f7dde51bd93f55a5285c6dbd8c063219a25f

I am not quite sure what's going on here, but I can verify that the image I pulled as 0.9.0 has this on lines 177 - 180:

        else:
            return f'<a href="{html.escape(href)}" rel="nofollow">{html.escape(content)}</a>'

    def create_mention(self, handle, href: str | None = None) -> str:

What do you see in the explicitly pulled 0.9.0 in that file on those lines?

@doodlemania2
Copy link
Author

That looks correct:

image

I wonder if the head and tail command weren't quite right? Either way, glad we sorted that issue, now to figure out why my instance is not hitting that correctly!

@virtualmarc
Copy link

This also happens on Takahe 0.10.1 on the Tags page.
Found this in my log while investigating another error:

ERROR:django.request:Internal Server Error: /tags/nextcloud/
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/django/core/handlers/exception.py", line 55, in inner
    response = get_response(request)
               ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/core/handlers/base.py", line 220, in _get_response
    response = response.render()
               ^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/response.py", line 114, in render
    self.content = self.rendered_content
                   ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/response.py", line 92, in rendered_content
    return template.render(context, self._request)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/backends/django.py", line 61, in render
    return self.template.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 175, in render
    return self._render(context)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 167, in _render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/loader_tags.py", line 157, in render
    return compiled_parent._render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 167, in _render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/loader_tags.py", line 63, in render
    result = block.nodelist.render(context)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/loader_tags.py", line 63, in render
    result = block.nodelist.render(context)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/defaulttags.py", line 238, in render
    nodelist.append(node.render_annotated(context))
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/loader_tags.py", line 208, in render
    return template.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 177, in render
    return self._render(context)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 167, in _render
    return self.nodelist.render(context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 1005, in render
    return SafeString("".join([node.render_annotated(context) for node in self]))
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 1005, in <listcomp>
    return SafeString("".join([node.render_annotated(context) for node in self]))
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 966, in render_annotated
    return self.render(context)
           ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 1064, in render
    output = self.filter_expression.resolve(context)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 715, in resolve
    obj = self.var.resolve(context)
          ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 847, in resolve
    value = self._resolve_lookup(context)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/django/template/base.py", line 914, in _resolve_lookup
    current = current()
              ^^^^^^^^^
  File "/takahe/activities/models/post.py", line 447, in safe_content_local
    return self.safe_content(local=True)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/takahe/activities/models/post.py", line 440, in safe_content
    return func(local=local)
           ^^^^^^^^^^^^^^^^^
  File "/takahe/activities/models/post.py", line 408, in _safe_content_note
    return ContentRenderer(local=local).render_post(self.content, self)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/takahe/core/html.py", line 340, in render_post
    parser = FediverseHtmlParser(
             ^^^^^^^^^^^^^^^^^^^^
  File "/takahe/core/html.py", line 81, in __init__
    self.feed(html.replace("\n", ""))
  File "/usr/local/lib/python3.11/html/parser.py", line 110, in feed
    self.goahead(0)
  File "/usr/local/lib/python3.11/html/parser.py", line 172, in goahead
    k = self.parse_endtag(i)
        ^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/html/parser.py", line 413, in parse_endtag
    self.handle_endtag(elem)
  File "/takahe/core/html.py", line 140, in handle_endtag
    self.html_output += self.create_link(
                        ^^^^^^^^^^^^^^^^^
  File "/takahe/core/html.py", line 180, in create_link
    return f'<a href="{html.escape(href)}" rel="nofollow">{html.escape(content)}</a>'
                       ^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/html/__init__.py", line 19, in escape
    s = s.replace("&", "&amp;") # Must be done first!
        ^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'replace'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants