Enable to fetch user info using REST API

since few days I am not able to fetch user info using the REST API with a token.

This code is not working anymore:

import requests

api_url = 'http://127.0.0.1:8081/hub/api'

r = requests.get(api_url + '/users',
    headers={
        'Authorization': f'token {token}',
    }
)

r.raise_for_status()
users = r.json()
HTTPError: 500 Server Error: Internal Server Error for url: https://server/hub/api/users

The other endpoints work fine e.g /users/{name}or /info only fetch all users /users is not working.

jupyterhub 1.5.0

You’re using a very old version of JupyterHub. Can you try the latest version?

Can you show us your full code, e.g. where are you obtaining the token from?

Can you turn on debug logging and show us your logs?

1 Like

Thanks @manics for looking into this, I know I am using an old version but it’s not possible to make un upgrade of the production server. It was working until weeks ago.
And it is also working on anther jupyterhub server running 1.5.0 version.

Jan 18 14:49:28 jupyterhub[168037]: [E 2024-01-18 14:49:28.545 JupyterHub web:1789] Uncaught exception GET /hub/api/users (::1)
Jan 18 14:49:28 jupyterhub[168037]:     HTTPServerRequest(protocol='http', host='xxx', method='GET', uri='/hub/api/users', version='HTTP/1.1', remote_ip='::1')
Jan 18 14:49:28 jupyterhub[168037]:     Traceback (most recent call last):
Jan 18 14:49:28 jupyterhub[168037]:       File "/home/jupyterhub/.conda/envs/jupyterhub/lib/python3.9/site-packages/tornado/web.py", line 1702, in _execute
Jan 18 14:49:28 jupyterhub[168037]:         result = method(*self.path_args, **self.path_kwargs)
Jan 18 14:49:28 jupyterhub[168037]:       File "/home/jupyterhub/.conda/envs/jupyterhub/lib/python3.9/site-packages/jupyterhub/utils.py", line 257, in decorated
Jan 18 14:49:28 jupyterhub[168037]:         return method(self, *args, **kwargs)
Jan 18 14:49:28 jupyterhub[168037]:       File "/home/jupyterhub/.conda/envs/jupyterhub/lib/python3.9/site-packages/jupyterhub/apihandlers/users.py", line 103, in get
Jan 18 14:49:28 jupyterhub[168037]:         self.write(json.dumps(data))
Jan 18 14:49:28 jupyterhub[168037]:       File "/home/jupyterhub/.conda/envs/jupyterhub/lib/python3.9/json/__init__.py", line 231, in dumps
Jan 18 14:49:28 jupyterhub[168037]:         return _default_encoder.encode(obj)
Jan 18 14:49:28 jupyterhub[168037]:       File "/home/jupyterhub/.conda/envs/jupyterhub/lib/python3.9/json/encoder.py", line 199, in encode
Jan 18 14:49:28 jupyterhub[168037]:         chunks = self.iterencode(o, _one_shot=True)
Jan 18 14:49:28 jupyterhub[168037]:       File "/home/jupyterhub/.conda/envs/jupyterhub/lib/python3.9/json/encoder.py", line 257, in iterencode
Jan 18 14:49:28 jupyterhub[168037]:         return _iterencode(o, 0)
Jan 18 14:49:28 jupyterhub[168037]:       File "/home/jupyterhub/.conda/envs/jupyterhub/lib/python3.9/json/encoder.py", line 179, in default
Jan 18 14:49:28 jupyterhub[168037]:         raise TypeError(f'Object of type {o.__class__.__name__} '
Jan 18 14:49:28  jupyterhub[168037]:     TypeError: Object of type set is not JSON serializable

We’d need more info about the deployment configuration, but something is the users model is a set when it shouldn’t be (auth state and spawner state are good candidates). For debugging, you can insert a log statement right before this json.dumps to print a repr of data and find out what’s a set that should be a list, and we can see if there’s any workaround available.

For example:

try:
    self.write(json.dumps(data))
except Exception:
    self.log.error("Error serializing %r", data)
    raise

@minrk Thank you for the advice, it helped me to write a workaround. It consists of a helper class to serialize data before json.dumps. Still concerned why the error appeared suddenly.

it would still help to see the error, but since you are on an extremely outdated version of JupyterHub, I’ll assume this has been fixed in more recent JupyterHub.

I am curious how a helper class is able to solve the problem, when it ought to be purely internal to JupyterHub in code extensions shouldn’t really be able to modify.

I just mean that I added a serializer before the json.dumps, something like this:

class SetEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, set):
            return list(obj)
        return json.JSONEncoder.default(self, obj)