#1829·visdom

Uncaught KeyError (HTTP 500) in CloseHandler and DataHandler for non-existent environments

Author: Pcmhacker-piroCreated Sep 14, 2026Updated Sep 14, 2026

Bug Description In py/visdom/server/handlers/web_handlers.py, both CloseHandler (POST /close) and DataHandler (POST /win_data) access handler.state[eid] directly without checking whether eid exists in handler.state.

When a request is made for an environment that does not exist or is not loaded:

  1. In CloseHandler.post:
    python
    await ensure_env_loaded(self, extract_eid(args))
    self.wrap_func(self, args)
    If the environment does not exist, ensure_env_loaded returns None immediately.
  2. In CloseHandler.wrap_func:
    python
    keys = list(handler.state[eid]["jsons"].keys()) if win is None else [win]
    for win in keys:
        p_data = handler.state[eid]["jsons"].pop(win, None)
    Whether win is None or a specific window name, accessing handler.state[eid] immediately raises an unhandled KeyError: '<eid>'.
  3. In DataHandler.wrap_func (when reading window data):
    python
    if "win" in args and args["win"] is None:
        handler.write(json.dumps(handler.state[eid]["jsons"], cls=NanSafeEncoder))
    else:
        if args["win"] not in handler.state[eid]["jsons"]:
            raise tornado.web.HTTPError(400, reason="window doesn't exist in this env")
    Accessing handler.state[eid] raises KeyError: '<eid>' before the window check can run.

Because this KeyError is uncaught, Tornado returns an HTTP 500 Internal Server Error instead of returning a 400/404 or gracefully no-opping. In contrast, other handlers like ExistsHandler and EnvStateHandler properly guard against missing environments with if eid in handler.state:.

Reproduction Steps

  1. Start the Visdom server (python -m visdom.server).
  2. Send a POST request to /close targeting a non-existent environment:
    bash
    curl -X POST http://localhost:8097/close \
      -H "Content-Type: application/json" \
      -d '{"eid": "nonexistent_env", "win": "w1"}'
    Or to clear a non-existent env:
    bash
    curl -X POST http://localhost:8097/close \
      -H "Content-Type: application/json" \
      -d '{"eid": "nonexistent_env"}'
  3. Send a POST request to /win_data for a non-existent environment:
    bash
    curl -X POST http://localhost:8097/win_data \
      -H "Content-Type: application/json" \
      -d '{"eid": "nonexistent_env", "win": "w1"}'
  4. Observe the response: HTTP 500 Internal Server Error.

Expected behavior

  • Closing a window or clearing an environment that does not exist should gracefully succeed/no-op (if eid not in handler.state: return), as there are no windows or state to delete.
  • Querying window data from an environment that does not exist should return HTTP 404 (e.g. tornado.web.HTTPError(404, reason="environment '...' not found")) or HTTP 400 rather than crashing with 500.

Server logs:

ERROR:tornado.application:Uncaught exception POST /close (127.0.0.1)
HTTPServerRequest(protocol='http', host='localhost:8097', method='POST', uri='/close', version='HTTP/1.1', remote_ip='127.0.0.1')
Traceback (most recent call last):
  File ".../tornado/web.py", line 1788, in _execute
    result = method(*self.path_args, **self.path_kwargs)
  File "py/visdom/server/handlers/web_handlers.py", line 538, in post
    self.wrap_func(self, args)
  File "py/visdom/server/handlers/web_handlers.py", line 523, in wrap_func
    keys = list(handler.state[eid]["jsons"].keys()) if win is None else [win]
KeyError: 'nonexistent_env'

Proposed Fix In py/visdom/server/handlers/web_handlers.py:

  1. In CloseHandler.wrap_func:
python
@staticmethod
def wrap_func(handler, args):
    eid = extract_eid(args)
    if eid not in handler.state:
        return
    win = args.get("win")
    ...
  1. In DataHandler.wrap_func:
python
@staticmethod
def wrap_func(handler, args):
    eid = extract_eid(args)
    if "data" in args:
        ...
    else:
        if eid not in handler.state:
            if "win" in args and args["win"] is None:
                handler.write(json.dumps({}, cls=NanSafeEncoder))
                return
            raise tornado.web.HTTPError(404, reason="environment '{}' not found".format(eid))
        ...