{"record":{"id":"5395f0c735975a30","repo":"assafelovic/gpt-researcher","slug":"rate-limit-delay-must-be-non-negative-got-delay","errorCode":null,"errorMessage":"rate_limit_delay must be non-negative, got {delay}","messagePattern":"rate_limit_delay must be non-negative, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"gpt_researcher/utils/rate_limiter.py","lineNumber":69,"sourceCode":"    def configure(self, rate_limit_delay: float):\n        \"\"\"\n        Configure the global rate limit delay.\n\n        Args:\n            rate_limit_delay: Minimum seconds between requests (0 = no limit)\n        \"\"\"\n        # Env/config may hand us strings; wait_if_needed compares as float.\n        if rate_limit_delay is None:\n            self.rate_limit_delay = 0.0\n            return\n        try:\n            delay = float(rate_limit_delay)\n        except (TypeError, ValueError) as e:\n            raise ValueError(\n                f\"rate_limit_delay must be a number, got {rate_limit_delay!r}\"\n            ) from e\n        if delay < 0:\n            raise ValueError(f\"rate_limit_delay must be non-negative, got {delay}\")\n        self.rate_limit_delay = delay\n\n    async def wait_if_needed(self):\n        \"\"\"\n        Wait if needed to enforce global rate limiting.\n\n        This method ensures that regardless of how many WorkerPools are active,\n        the SCRAPER_RATE_LIMIT_DELAY is respected globally.\n        \"\"\"\n        if self.rate_limit_delay <= 0:\n            return  # No rate limiting\n\n        lock = self.get_lock()\n        async with lock:\n            current_time = time.time()\n            time_since_last = current_time - self.last_request_time\n\n            if time_since_last < self.rate_limit_delay:","sourceCodeStart":51,"sourceCodeEnd":87,"githubUrl":"https://github.com/assafelovic/gpt-researcher/blob/6f998577d547b1e54ec662dac63583aa11e3b84b/gpt_researcher/utils/rate_limiter.py#L51-L87","documentation":"RateLimiter.configure raises ValueError when rate_limit_delay parses to a float but is negative. Delays are durations, so negative values are meaningless and usually indicate a sign typo or a bad env var like RATE_LIMIT_DELAY=-1.","triggerScenarios":"Passing rate_limit_delay=-0.5 or a string like '-1' (which float() accepts and then fails the `delay < 0` check) to RateLimiter/ configure(); often sourced from RATE_LIMIT_DELAY in env or config.","commonSituations":"Typo'd negative sign in an env var, attempting to use -1 as a 'disabled' sentinel, or arithmetic elsewhere in config that computed a negative delay.","solutions":["Use a non-negative value; 0 means no delay","If 'disabled' was the intent, set rate_limit_delay=None or 0","Fix RATE_LIMIT_DELAY in your .env (remove the minus sign)","Sanitize at startup: max(0.0, float(os.getenv('RATE_LIMIT_DELAY', 0)))"],"exampleFix":"# before\nlimiter = RateLimiter('-1')  # ValueError: rate_limit_delay must be non-negative, got -1.0\n\n# after\nlimiter = RateLimiter('0')  # no delay","handlingStrategy":"validation","validationCode":"import os\nraw = os.getenv('RATE_LIMIT_DELAY', '0')\ndelay = max(0.0, float(raw))  # clamps accidental negatives","typeGuard":"def valid_delay(v) -> bool:\n    try:\n        return float(v) >= 0\n    except (TypeError, ValueError):\n        return v is None","tryCatchPattern":"try:\n    limiter = RateLimiter(raw)\nexcept ValueError:\n    limiter = RateLimiter(0)  # safe default, log the bad value","preventionTips":["Never use negative numbers to disable the limiter — use 0 or None","Range-check config numbers at load time and clamp to sane bounds","Watch for stray minus signs when copying config values between environments"],"tags":["validation","rate-limit","configuration","negative-value"],"backgroundTag":"invalid-numeric-config-value","analyzedSha":"6f998577d547b1e54ec662dac63583aa11e3b84b","analyzedAt":"2026-08-28T17:50:07.383Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}