prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  nautobot-scheduler can't apply celery_backend_cleanup_1
  <!--
      NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED.
  
      This form is only for reporting reproducible bugs. If you need assistance
      with Nautobot installation, or if you have a general question, please start a
      discussion instead: https://github.com/nautobot/nautobot/discussions
  
      Please describe the environment in which you are running Nautobot. Be sure
      that you are running an unmodified instance of the latest stable release
      before submitting a bug report, and that any plugins have been disabled.
  -->
  ### Environment
  * Python version: 3.9
  * Nautobot version: 1.2.5
  
  <!--
      Describe in detail the exact steps that someone else can take to reproduce
      this bug using the current stable release of Nautobot. Begin with the
      creation of any necessary database objects and call out every operation
      being performed explicitly. If reporting a bug in the REST API, be sure to
      reconstruct the raw HTTP request(s) being made: Don't rely on a client
      library such as pynautobot.
  -->
  ### Steps to Reproduce
  1. Run the scheduler as systemd-service
  2.
  3.
  
  <!-- What did you expect to happen? -->
  ### Expected Behavior
  Internal jobs run without an error
  
  <!-- What happened instead? -->
  ### Observed Behavior
  ```
  Feb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]: [2022-02-08 04:00:00,000: INFO/MainProcess] Scheduler: Sending due task celery.backend_cleanup_1 (celery.backend_cleanup)
  Feb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]: [2022-02-08 04:00:00,001: ERROR/MainProcess] Message Error: Couldn't apply scheduled task celery.backend_cleanup_1: 'str' object has no attribute 'items'
  Feb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]: ['  File "/opt/nautobot/bin/nautobot-server", line 8, in <module>\n    sys.exit(main())\n', '  File "/opt/nautobot/lib/python3.9/site-packages/nautobot/cor>
  Feb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]: Traceback (most recent call last):
  Feb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]:   File "/opt/nautobot/lib/python3.9/site-packages/celery/beat.py", line 402, in apply_async
  Feb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]:     entry_kwargs = _evaluate_entry_kwargs(entry.kwargs)
  Feb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]:   File "/opt/nautobot/lib/python3.9/site-packages/celery/beat.py", line 220, in _evaluate_entry_kwargs
  Feb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]:     for k, v in entry_kwargs.items()
  Feb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]: AttributeError: 'str' object has no attribute 'items'
  ```
  
  I see this error for every day.
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `nautobot/core/celery/schedulers.py`
  Content:
  ```
  1 import logging
  2 
  3 from celery import current_app
  4 from django_celery_beat.schedulers import ModelEntry, DatabaseScheduler
  5 
  6 from nautobot.extras.models import ScheduledJob, ScheduledJobs
  7 
  8 
  9 logger = logging.getLogger(__name__)
  10 
  11 
  12 class NautobotScheduleEntry(ModelEntry):
  13     """
  14     Nautobot variant of the django-celery-beat ModelEntry which uses the
  15     nautobot.extras.models.ScheduledJob model
  16     """
  17 
  18     def __init__(self, model, app=None):
  19         """Initialize the model entry."""
  20         self.app = app or current_app._get_current_object()
  21         self.name = "{}_{}".format(model.name, model.pk)
  22         self.task = model.task
  23         self.args = model.args
  24         self.kwargs = model.kwargs
  25         try:
  26             self.schedule = model.schedule
  27         except model.DoesNotExist:
  28             logger.error(
  29                 "Disabling schedule %s that was removed from database",
  30                 self.name,
  31             )
  32             self._disable(model)
  33 
  34         self.options = {}
  35         if model.queue:
  36             self.options["queue"] = model.queue
  37 
  38         self.options["headers"] = {}
  39         self.total_run_count = model.total_run_count
  40         self.model = model
  41 
  42         if not model.last_run_at:
  43             model.last_run_at = self._default_now()
  44 
  45         self.last_run_at = model.last_run_at
  46 
  47 
  48 class NautobotDatabaseScheduler(DatabaseScheduler):
  49     """
  50     Nautobot variant of the django-celery-beat DatabaseScheduler which uses the
  51     nautobot.extras.models.ScheduledJob model
  52     """
  53 
  54     Entry = NautobotScheduleEntry
  55     Model = ScheduledJob
  56     Changes = ScheduledJobs
  57
  ```
  --- END FILES ---
  
  Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.
  
  Here is an example:
  
  ```diff
  diff --git a/examples/server_async.py b/examples/server_async.py
  --- a/examples/server_async.py
  +++ b/examples/server_async.py
  @@ -313,4 +313,4 @@
   
   
   if __name__ == "__main__":
  -    asyncio.run(run_async_server("."), debug=True)
  +    asyncio.run(run_async_server(), debug=True)
  diff --git a/examples/server_sync.py b/examples/server_sync.py
  --- a/examples/server_sync.py
  +++ b/examples/server_sync.py
  @@ -313,5 +313,5 @@
   
   
   if __name__ == "__main__":
  -    server = run_sync_server(".")
  +    server = run_sync_server()
       server.shutdown()
  
  ```
  
golden_diff: diff --git a/nautobot/core/celery/schedulers.py b/nautobot/core/celery/schedulers.py
  --- a/nautobot/core/celery/schedulers.py
  +++ b/nautobot/core/celery/schedulers.py
  @@ -2,6 +2,7 @@
   
   from celery import current_app
   from django_celery_beat.schedulers import ModelEntry, DatabaseScheduler
  +from kombu.utils.json import loads
   
   from nautobot.extras.models import ScheduledJob, ScheduledJobs
   
  @@ -20,8 +21,14 @@
           self.app = app or current_app._get_current_object()
           self.name = "{}_{}".format(model.name, model.pk)
           self.task = model.task
  -        self.args = model.args
  -        self.kwargs = model.kwargs
  +        try:
  +            # Nautobot scheduled jobs pass args/kwargs as constructed objects,
  +            # but Celery built-in jobs such as celery.backend_cleanup pass them as JSON to be parsed
  +            self.args = model.args if isinstance(model.args, (tuple, list)) else loads(model.args or "[]")
  +            self.kwargs = model.kwargs if isinstance(model.kwargs, dict) else loads(model.kwargs or "{}")
  +        except (TypeError, ValueError) as exc:
  +            logger.exception("Removing schedule %s for argument deserialization error: %s", self.name, exc)
  +            self._disable(model)
           try:
               self.schedule = model.schedule
           except model.DoesNotExist:
  
verification_info: {"golden_diff": "diff --git a/nautobot/core/celery/schedulers.py b/nautobot/core/celery/schedulers.py\n--- a/nautobot/core/celery/schedulers.py\n+++ b/nautobot/core/celery/schedulers.py\n@@ -2,6 +2,7 @@\n \n from celery import current_app\n from django_celery_beat.schedulers import ModelEntry, DatabaseScheduler\n+from kombu.utils.json import loads\n \n from nautobot.extras.models import ScheduledJob, ScheduledJobs\n \n@@ -20,8 +21,14 @@\n         self.app = app or current_app._get_current_object()\n         self.name = \"{}_{}\".format(model.name, model.pk)\n         self.task = model.task\n-        self.args = model.args\n-        self.kwargs = model.kwargs\n+        try:\n+            # Nautobot scheduled jobs pass args/kwargs as constructed objects,\n+            # but Celery built-in jobs such as celery.backend_cleanup pass them as JSON to be parsed\n+            self.args = model.args if isinstance(model.args, (tuple, list)) else loads(model.args or \"[]\")\n+            self.kwargs = model.kwargs if isinstance(model.kwargs, dict) else loads(model.kwargs or \"{}\")\n+        except (TypeError, ValueError) as exc:\n+            logger.exception(\"Removing schedule %s for argument deserialization error: %s\", self.name, exc)\n+            self._disable(model)\n         try:\n             self.schedule = model.schedule\n         except model.DoesNotExist:\n", "issue": "nautobot-scheduler can't apply celery_backend_cleanup_1\n<!--\r\n    NOTE: IF YOUR ISSUE DOES NOT FOLLOW THIS TEMPLATE, IT WILL BE CLOSED.\r\n\r\n    This form is only for reporting reproducible bugs. If you need assistance\r\n    with Nautobot installation, or if you have a general question, please start a\r\n    discussion instead: https://github.com/nautobot/nautobot/discussions\r\n\r\n    Please describe the environment in which you are running Nautobot. Be sure\r\n    that you are running an unmodified instance of the latest stable release\r\n    before submitting a bug report, and that any plugins have been disabled.\r\n-->\r\n### Environment\r\n* Python version: 3.9\r\n* Nautobot version: 1.2.5\r\n\r\n<!--\r\n    Describe in detail the exact steps that someone else can take to reproduce\r\n    this bug using the current stable release of Nautobot. Begin with the\r\n    creation of any necessary database objects and call out every operation\r\n    being performed explicitly. If reporting a bug in the REST API, be sure to\r\n    reconstruct the raw HTTP request(s) being made: Don't rely on a client\r\n    library such as pynautobot.\r\n-->\r\n### Steps to Reproduce\r\n1. Run the scheduler as systemd-service\r\n2.\r\n3.\r\n\r\n<!-- What did you expect to happen? -->\r\n### Expected Behavior\r\nInternal jobs run without an error\r\n\r\n<!-- What happened instead? -->\r\n### Observed Behavior\r\n```\r\nFeb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]: [2022-02-08 04:00:00,000: INFO/MainProcess] Scheduler: Sending due task celery.backend_cleanup_1 (celery.backend_cleanup)\r\nFeb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]: [2022-02-08 04:00:00,001: ERROR/MainProcess] Message Error: Couldn't apply scheduled task celery.backend_cleanup_1: 'str' object has no attribute 'items'\r\nFeb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]: ['  File \"/opt/nautobot/bin/nautobot-server\", line 8, in <module>\\n    sys.exit(main())\\n', '  File \"/opt/nautobot/lib/python3.9/site-packages/nautobot/cor>\r\nFeb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]: Traceback (most recent call last):\r\nFeb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]:   File \"/opt/nautobot/lib/python3.9/site-packages/celery/beat.py\", line 402, in apply_async\r\nFeb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]:     entry_kwargs = _evaluate_entry_kwargs(entry.kwargs)\r\nFeb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]:   File \"/opt/nautobot/lib/python3.9/site-packages/celery/beat.py\", line 220, in _evaluate_entry_kwargs\r\nFeb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]:     for k, v in entry_kwargs.items()\r\nFeb 08 04:00:00 dh01-a-06-18 nautobot-server[3033678]: AttributeError: 'str' object has no attribute 'items'\r\n```\r\n\r\nI see this error for every day.\n", "before_files": [{"content": "import logging\n\nfrom celery import current_app\nfrom django_celery_beat.schedulers import ModelEntry, DatabaseScheduler\n\nfrom nautobot.extras.models import ScheduledJob, ScheduledJobs\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass NautobotScheduleEntry(ModelEntry):\n    \"\"\"\n    Nautobot variant of the django-celery-beat ModelEntry which uses the\n    nautobot.extras.models.ScheduledJob model\n    \"\"\"\n\n    def __init__(self, model, app=None):\n        \"\"\"Initialize the model entry.\"\"\"\n        self.app = app or current_app._get_current_object()\n        self.name = \"{}_{}\".format(model.name, model.pk)\n        self.task = model.task\n        self.args = model.args\n        self.kwargs = model.kwargs\n        try:\n            self.schedule = model.schedule\n        except model.DoesNotExist:\n            logger.error(\n                \"Disabling schedule %s that was removed from database\",\n                self.name,\n            )\n            self._disable(model)\n\n        self.options = {}\n        if model.queue:\n            self.options[\"queue\"] = model.queue\n\n        self.options[\"headers\"] = {}\n        self.total_run_count = model.total_run_count\n        self.model = model\n\n        if not model.last_run_at:\n            model.last_run_at = self._default_now()\n\n        self.last_run_at = model.last_run_at\n\n\nclass NautobotDatabaseScheduler(DatabaseScheduler):\n    \"\"\"\n    Nautobot variant of the django-celery-beat DatabaseScheduler which uses the\n    nautobot.extras.models.ScheduledJob model\n    \"\"\"\n\n    Entry = NautobotScheduleEntry\n    Model = ScheduledJob\n    Changes = ScheduledJobs\n", "path": "nautobot/core/celery/schedulers.py"}], "after_files": [{"content": "import logging\n\nfrom celery import current_app\nfrom django_celery_beat.schedulers import ModelEntry, DatabaseScheduler\nfrom kombu.utils.json import loads\n\nfrom nautobot.extras.models import ScheduledJob, ScheduledJobs\n\n\nlogger = logging.getLogger(__name__)\n\n\nclass NautobotScheduleEntry(ModelEntry):\n    \"\"\"\n    Nautobot variant of the django-celery-beat ModelEntry which uses the\n    nautobot.extras.models.ScheduledJob model\n    \"\"\"\n\n    def __init__(self, model, app=None):\n        \"\"\"Initialize the model entry.\"\"\"\n        self.app = app or current_app._get_current_object()\n        self.name = \"{}_{}\".format(model.name, model.pk)\n        self.task = model.task\n        try:\n            # Nautobot scheduled jobs pass args/kwargs as constructed objects,\n            # but Celery built-in jobs such as celery.backend_cleanup pass them as JSON to be parsed\n            self.args = model.args if isinstance(model.args, (tuple, list)) else loads(model.args or \"[]\")\n            self.kwargs = model.kwargs if isinstance(model.kwargs, dict) else loads(model.kwargs or \"{}\")\n        except (TypeError, ValueError) as exc:\n            logger.exception(\"Removing schedule %s for argument deserialization error: %s\", self.name, exc)\n            self._disable(model)\n        try:\n            self.schedule = model.schedule\n        except model.DoesNotExist:\n            logger.error(\n                \"Disabling schedule %s that was removed from database\",\n                self.name,\n            )\n            self._disable(model)\n\n        self.options = {}\n        if model.queue:\n            self.options[\"queue\"] = model.queue\n\n        self.options[\"headers\"] = {}\n        self.total_run_count = model.total_run_count\n        self.model = model\n\n        if not model.last_run_at:\n            model.last_run_at = self._default_now()\n\n        self.last_run_at = model.last_run_at\n\n\nclass NautobotDatabaseScheduler(DatabaseScheduler):\n    \"\"\"\n    Nautobot variant of the django-celery-beat DatabaseScheduler which uses the\n    nautobot.extras.models.ScheduledJob model\n    \"\"\"\n\n    Entry = NautobotScheduleEntry\n    Model = ScheduledJob\n    Changes = ScheduledJobs\n", "path": "nautobot/core/celery/schedulers.py"}]}