# Buggy code
def poll(self):
    """
    Check if the pod is still running.

    Uses the same interface as subprocess.Popen.poll(): if the pod is
    still running, returns None.  If the pod has exited, return the
    exit code if we can determine it, or 1 if it has exited but we
    don't know how.  These are the return values JupyterHub expects.

    Note that a clean exit will have an exit code of zero, so it is
    necessary to check that the returned value is None, rather than
    just Falsy, to determine that the pod is still running.
    """
    # have to wait for first load of data before we have a valid answer
    if not self.pod_reflector.first_load_future.done():
        yield self.pod_reflector.first_load_future
    data = self.pod_reflector.pods.get(self.pod_name, None)
    if data is not None:
        if data["status"]["phase"] == "Pending":
            return None
        ctr_stat = data["status"]["containerStatuses"]
        if ctr_stat is None:  # No status, no container (we hope)
            # This seems to happen when a pod is idle-culled.
            return 1
        for c in ctr_stat:
            # return exit code if notebook container has terminated
            if c["name"] == "notebook":
                if "terminated" in c["state"]:
                    # call self.stop to delete the pod
                    if self.delete_stopped_pods:
                        yield self.stop(now=True)
                    return c["state"]["terminated"]["exitCode"]
                break
        # None means pod is running or starting up
        return None
    # pod doesn't exist or has been deleted
    return 1


# Traceback (KeyError)
Sep 29 15:12:00hub-6788d79c4-mct9jhubERRORERROR 2020-09-29T20:12:00.742Z [JupyterHub user:645] Unhandled error starting 5f5118929d1395001a6aa4df's server: pod/jupyter-5f5118929d1395001a6aa4df did not start in 300 seconds!
Sep 29 15:12:00hub-6788d79c4-mct9jhubERRORERROR 2020-09-29T20:12:00.778Z [JupyterHub user:657] Failed to cleanup 5f5118929d1395001a6aa4df's server that failed to start
Traceback (most recent call last):
File "/usr/local/lib/python3.6/dist-packages/jupyterhub/user.py", line 651, in spawn
await self.stop(spawner.name)
File "/usr/local/lib/python3.6/dist-packages/jupyterhub/user.py", line 748, in stop
status = await spawner.poll()
File "/usr/local/lib/python3.6/dist-packages/kubespawner/spawner.py", line 1572, in poll
ctr_stat = data["status"]["containerStatuses"]
KeyError: 'containerStatuses'

Sep 29 15:12:00hub-6788d79c4-mct9jhubERRORERROR 2020-09-29T20:12:00.779Z [JupyterHub gen:599] Exception in Future <Task finished coro=<BaseHandler.spawn_single_user.<locals>.finish_user_spawn() done, defined at /usr/local/lib/python3.6/dist-packages/jupyterhub/handlers/base.py:845> exception=TimeoutError('pod/jupyter-5f5118929d1395001a6aa4df did not start in 300 seconds!',)> after timeout
Traceback (most recent call last):
File "/usr/local/lib/python3.6/dist-packages/tornado/gen.py", line 593, in error_callback
future.result()
File "/usr/local/lib/python3.6/dist-packages/jupyterhub/handlers/base.py", line 852, in finish_user_spawn
await spawn_future
File "/usr/local/lib/python3.6/dist-packages/jupyterhub/user.py", line 661, in spawn
raise e
File "/usr/local/lib/python3.6/dist-packages/jupyterhub/user.py", line 560, in spawn
url = await gen.with_timeout(timedelta(seconds=spawner.start_timeout), f)
File "/usr/local/lib/python3.6/dist-packages/kubespawner/spawner.py", line 1854, in _start
timeout=self.start_timeout,
File "/usr/local/lib/python3.6/dist-packages/jupyterhub/utils.py", line 177, in exponential_backoff
raise TimeoutError(fail_message)
TimeoutError: pod/jupyter-5f5118929d1395001a6aa4df did not start in 300 seconds!

# Fixed code
def poll(self):
    """
    Check if the pod is still running.

    Uses the same interface as subprocess.Popen.poll(): if the pod is
    still running, returns None.  If the pod has exited, return the
    exit code if we can determine it, or 1 if it has exited but we
    don't know how.  These are the return values JupyterHub expects.

    Note that a clean exit will have an exit code of zero, so it is
    necessary to check that the returned value is None, rather than
    just Falsy, to determine that the pod is still running.
    """
    # have to wait for first load of data before we have a valid answer
    if not self.pod_reflector.first_load_future.done():
        yield self.pod_reflector.first_load_future
    data = self.pod_reflector.pods.get(self.pod_name, None)
    if data is not None:
        if data["status"]["phase"] == "Pending":
            return None
        ctr_stat = data["status"].get("containerStatuses")
        if ctr_stat is None:  # No status, no container (we hope)
            # This seems to happen when a pod is idle-culled.
            return 1
        for c in ctr_stat:
            # return exit code if notebook container has terminated
            if c["name"] == "notebook":
                if "terminated" in c["state"]:
                    # call self.stop to delete the pod
                    if self.delete_stopped_pods:
                        yield self.stop(now=True)
                    return c["state"]["terminated"]["exitCode"]
                break
        # None means pod is running or starting up
        return None
    # pod doesn't exist or has been deleted
    return 1