{"record":{"id":"79e19c22113406a8","repo":"RustPython/RustPython","slug":"delay-must-not-be-none","errorCode":null,"errorMessage":"delay must not be None","messagePattern":"delay must not be None","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"Lib/asyncio/base_events.py","lineNumber":792,"sourceCode":"\n    def call_later(self, delay, callback, *args, context=None):\n        \"\"\"Arrange for a callback to be called at a given time.\n\n        Return a Handle: an opaque object with a cancel() method that\n        can be used to cancel the call.\n\n        The delay can be an int or float, expressed in seconds.  It is\n        always relative to the current time.\n\n        Each callback will be called exactly once.  If two callbacks\n        are scheduled for exactly the same time, it is undefined which\n        will be called first.\n\n        Any positional arguments after the callback will be passed to\n        the callback when it is called.\n        \"\"\"\n        if delay is None:\n            raise TypeError('delay must not be None')\n        timer = self.call_at(self.time() + delay, callback, *args,\n                             context=context)\n        if timer._source_traceback:\n            del timer._source_traceback[-1]\n        return timer\n\n    def call_at(self, when, callback, *args, context=None):\n        \"\"\"Like call_later(), but uses an absolute time.\n\n        Absolute time corresponds to the event loop's time() method.\n        \"\"\"\n        if when is None:\n            raise TypeError(\"when cannot be None\")\n        self._check_closed()\n        if self._debug:\n            self._check_thread()\n            self._check_callback(callback, 'call_at')\n        timer = events.TimerHandle(when, callback, args, self, context)","sourceCodeStart":774,"sourceCodeEnd":810,"githubUrl":"https://github.com/RustPython/RustPython/blob/aaeab4f754b4f40efc0c8ab39cf7c4a3c35a8cfd/Lib/asyncio/base_events.py#L774-L810","documentation":"loop.call_later(delay, callback, *args) requires a numeric delay in seconds. None is explicitly rejected with TypeError('delay must not be None') so that a missing delay fails fast with a clear message instead of the more obscure error that time() + None would produce.","triggerScenarios":"loop.call_later(delay, cb) where delay came from an optional parameter defaulted to None; passing None from configuration or a lookup that failed to produce a number.","commonSituations":"Wrapper functions like 'def retry(delay=None)' forwarding to call_later; config values that are absent and surface as None; refactors that change a delay's default from 0 to None.","solutions":["Default optional delays to 0 (or use loop.call_soon for immediate scheduling) instead of None","Validate before scheduling: if delay is None, raise or substitute a sensible default in your own code","Type-annotate delay as float and check isinstance(delay, (int, float)) at your API boundary","Trace where the None originated — usually an optional config or keyword argument"],"exampleFix":"# before\n# def schedule(delay, cb):\n#     loop.call_later(delay, cb)  # delay=None -> TypeError\n\n# after\n# def schedule(delay, cb):\n#     loop.call_later(delay or 0, cb)","handlingStrategy":"validation","validationCode":"if delay is None:\n    delay = 0.0\nelif not isinstance(delay, (int, float)):\n    raise TypeError(f'delay must be a number, got {delay!r}')\nloop.call_later(delay, callback)","typeGuard":"def is_delay(value) -> bool:\n    return isinstance(value, (int, float))","tryCatchPattern":null,"preventionTips":["Default optional delay parameters to 0, not None","Use loop.call_soon when you mean 'as soon as possible'","Validate config-sourced timing values at load time"],"tags":["asyncio","call-later","typeerror","validation"],"backgroundTag":"argument-type-error","analyzedSha":"aaeab4f754b4f40efc0c8ab39cf7c4a3c35a8cfd","analyzedAt":"2026-08-17T00:37:52.100Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}