Users.get_user() returns None in freshly launched background tasks

Hi all, this is a very peculiar and highly sporadic problem

What I’m trying to do:

  • Load the currently logged in user.

What I’ve tried and what’s not working:

  • I am using users.get_user()
  • The method has worked for months
  • The method is NOT working at the moment
  • The method has stopped working 3 hours ago (as per the writing on this post) - 15 July
  • The method stopped working for about 4 hours on 14 July
  • The method has worked with no problems outside of these intervals
  • I have a scheduled job that fires every hour and checks if a system user is logged in so I can pinpoint this problem down to the hour
  • Currently, the method does not return the logged in user regardless of whether I am using the built-in UI login form or the anvil.users.login_with_email method

There is really no specific code snipped that I can provide. Just a user that has logged in via UI or via the anvil.users.login_with_email is sometimes returned and sometimes not returned by users.get_user() without any changes on my part. The problem seems to have started occurring yesterday - 14 July.

Thank you

1 Like

Hi @krasen,

Thanks for the heads-up - we’re looking into this now. Just to confirm, how does your scheduled job work? Does it visit the page in a browser and check who is logged in, or is this some kind of Anvil Scheduled Task?

Thanks!

[Moved to Bug Reports]

Following up here, to help us identify the issue: Where are you calling anvil.users.get_user() from? Client code, server code or uplink? Thanks.

I can confirm this issue as well. I have a background task that authenticates users from server code that is consistently failing over the last 6 hours. Currently can’t pull data into my app because this authentication is required to parse data. As far as I can tell it’s only failing in server code. Any updates?

Please, have a look at the simple app here - https://anvil.works/build#clone:BRWUNHAYNAQDZUVS=ODDXQQBW5GAVTOOGWN3HBNTX It has a scheduled task for every minute that uses a system user (logs the user in). The inner task verifies what user is logged in. The idea is that the inner task can be invoked with an admin button and has to check for an admin user … for example. Again, this used to work for literally months in a row.

I can reproduce this problem every time.

Hi @krasen,

Thanks for the clone link - that’s really helped us narrow it down!

It looks like your app was relying on a bug in Anvil’s Background Tasks implementation, which has now been fixed. In this post, I’ll explain why, and how to update your app so it works again:

When you launch a Background Task, it launches in a separate session (as described in the docs), which means it has a separate anvil.server.session_state, and a separate idea of whether there is a user logged in. In the app you’ve shared, you’re logging in as the admin user and then launching the call_inner_task_background background task. The call_inner_task_background function is running in a completely new session that knows nothing about the user you’ve just logged in!

The fix is to log the user in inside the background task. In your case, it’s always the same user, so it’s a simple change. If you wanted to tell the background task which user to log in as, you could pass the row from the Users table into the task, and use anvil.users.force_login() to log yourself in.

In fact, your app doesn’t need to use App Secrets to store the email and password of the admin user – you can just do:

superuser = app_tables.users.get(email="superuser@yourapp.com")
anvil.users.force_login(superuser)

So, why was this working before? In earlier versions of the Anvil platform, there was a bug that in some circumstances caused “leakage” of information (including logged-in user status) from the “launching” session into the “launchee” session of the background task. We have recently released an update that, among other things, fixes this bug! If your app was working before, it looks like your app had tickled this bug, and was relying on its behaviour.

I’m sorry about the unpleasant surprise, but the previous behaviour was buggy and unreliable, so we can’t sensibly change it back. We try to avoid making updates that break code that worked on previous versions; this is one of the rare exceptions. The best way forward is to update your app as described above.

I have moved this post out of the Bug Reports section, as it’s not exactly a bug in Anvil – or at least, it isn’t any more!

4 Likes

Thank you!

Please, help me understand, how a background task that is not aware of the user that invoked it can “defend” itself against users that do not have the authorizations to actually run it? I can check for such a user in the invoking code … true. However, I cannot limit from where a background task can be invoked and I cannot decorate a background task with (require_user = is_logged_user) for example. What is your recommended way of implementing authorizations for background tasks? Thanks a lot.

1 Like

The crucial defence here is that you can only call anvil.server.launch_background_task() from Server Modules (ie trusted code). So you can do your checks (eg require_user) in the @anvil.server.callable functions (which can be called from untrusted code), before launching the background task. Then the background task can trust its input (because your code will only launch it on behalf of users it deems worthy).

I hope that helps – happy to explain more, but if you have more questions we should probably take it to a new Q&A thread :slight_smile:

2 Likes

Thank you for the rapid response.

1 Like