#1840·visdom

[Bug Report] Uncaught AttributeError (HTTP 500) in ForkEnvHandler when prev_eid or eid is missing

Author: Shlok1729Created Sep 17, 2026Updated Sep 17, 2026

Bug Description In py/visdom/server/handlers/web_handlers.py, ForkEnvHandler.wrap_func passes args.get("prev_eid") and args.get("eid") directly to escape_eid() without verifying that both fields are provided in the request payload:

python
class ForkEnvHandler(BaseHandler):
    @staticmethod
    async def wrap_func(handler, args):
        prev_eid = escape_eid(args.get("prev_eid"))
        eid = escape_eid(args.get("eid"))

When a request to POST /fork_env omits either prev_eid or eid (or passes null), escape_eid(None) attempts to execute eid.strip(). This immediately raises an uncaught AttributeError: 'NoneType' object has no attribute 'strip'.

Because this exception is unhandled, Tornado catches it at the application boundary and responds with HTTP 500 Internal Server Error instead of returning a clean client error (HTTP 400 Bad Request).

In contrast, other handlers in web_handlers.py guard against None before calling escape_eid (e.g., DeleteEnvHandler checks if eid is None: return None, and EnvStateHandler checks if eid is not None:).

Reproduction Steps Enter steps to reproduce the behavior:

  1. Start the Visdom server (python -m visdom.server).
  2. Send a POST request to /fork_env with prev_eid missing:
    bash
    curl -i -X POST http://localhost:8097/fork_env \
      -H "Content-Type: application/json" \
      -d '{"eid": "new_env"}'
    Or send a POST request with eid missing:
    bash
    curl -i -X POST http://localhost:8097/fork_env \
      -H "Content-Type: application/json" \
      -d '{"prev_eid": "main"}'
  3. Observe the response: HTTP 500 Internal Server Error.

Expected behavior ForkEnvHandler should validate that both prev_eid and eid are provided in the request body as strings. If either field is missing, null, or of invalid type, the server should return HTTP 400 Bad Request (e.g. raise tornado.web.HTTPError(400, reason="both 'prev_eid' and 'eid' must be strings")) rather than crashing with an unhandled 500.

Screenshots N/A (backend REST endpoint validation issue)

Client logs: N/A (applies to HTTP API requests)

Server logs:

Uncaught exception POST /fork_env (127.0.0.1)
HTTPServerRequest(protocol='http', host='127.0.0.1:8097', method='POST', uri='/fork_env', version='HTTP/1.1', remote_ip='127.0.0.1')
Traceback (most recent call last):
  File ".../site-packages/tornado/web.py", line 1886, in _execute
    result = await result
  File "py/visdom/server/handlers/web_handlers.py", line 638, in post
    await self.wrap_func(self, args)
  File "py/visdom/server/handlers/web_handlers.py", line 604, in wrap_func
    prev_eid = escape_eid(args.get("prev_eid"))
  File "py/visdom/utils/server_utils.py", line 493, in escape_eid
    eid.strip()
AttributeError: 'NoneType' object has no attribute 'strip'
ERROR:tornado.access:500 POST /fork_env (127.0.0.1) 2.85ms

Additional context According to openapi.yaml (lines 368–376), both prev_eid and eid are marked as required fields:

yaml
schema:
  type: object
  required: [prev_eid, eid]
  properties:
    prev_eid:
      type: string
    eid:
      type: string

Adding an explicit validation check in ForkEnvHandler.wrap_func:

python
prev_eid = args.get("prev_eid")
eid = args.get("eid")
if not isinstance(prev_eid, str) or not isinstance(eid, str):
    raise tornado.web.HTTPError(
        400, reason="both 'prev_eid' and 'eid' must be strings"
    )

prevents the crash and ensures alignment with the OpenAPI specification and standard REST behavior.