{"record":{"id":"03b9f6b9443a7121","repo":"HKUDS/Vibe-Trading","slug":"consecutive-failures-must-be-a-non-negative-inte","errorCode":null,"errorMessage":"'consecutive_failures' must be a non-negative integer","messagePattern":"'consecutive_failures' must be a non-negative integer","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"agent/src/scheduled_research/models.py","lineNumber":451,"sourceCode":"        job_id = data[\"id\"]\n        prompt = data[\"prompt\"]\n        schedule = data[\"schedule\"]\n        if not isinstance(job_id, str) or not isinstance(prompt, str) or not isinstance(schedule, str):\n            raise TypeError(\"'id', 'prompt', and 'schedule' must be strings\")\n        next_run_at = data[\"next_run_at\"]\n        created_at = data[\"created_at\"]\n        if not isinstance(next_run_at, int) or not isinstance(created_at, int):\n            raise TypeError(\"'next_run_at' and 'created_at' must be integers (epoch ms)\")\n        last_run_at = data.get(\"last_run_at\")\n        if last_run_at is not None and not isinstance(last_run_at, int):\n            raise TypeError(\"'last_run_at' must be an integer (epoch ms) or null\")\n        consecutive_failures = data.get(\"consecutive_failures\", 0)\n        if (\n            isinstance(consecutive_failures, bool)\n            or not isinstance(consecutive_failures, int)\n            or consecutive_failures < 0\n        ):\n            raise TypeError(\"'consecutive_failures' must be a non-negative integer\")\n        last_error = data.get(\"last_error\")\n        failure_kind = data.get(\"failure_kind\")\n        if last_error is not None and not isinstance(last_error, str):\n            raise TypeError(\"'last_error' must be a string or null\")\n        if failure_kind is not None and failure_kind not in {\"dispatch\", \"schedule\"}:\n            raise ValueError(\"'failure_kind' must be 'dispatch', 'schedule', or null\")\n        # Never raises: the store quarantines the whole file when a single\n        # record fails to load, so an unusable timezone value degrades that\n        # one job to UTC — the semantics it had before the field existed —\n        # instead of taking every other job down with it. Absent, blank, and\n        # non-string values all normalize to None.\n        raw_tz = data.get(\"timezone\")\n        tz = raw_tz if isinstance(raw_tz, str) and raw_tz.strip() else None\n        if raw_tz is not None and tz is None:\n            logger.warning(\n                \"scheduled research job %s has an unusable timezone %r; \"\n                \"evaluating its schedule in UTC\",\n                job_id,","sourceCodeStart":433,"sourceCodeEnd":469,"githubUrl":"https://github.com/HKUDS/Vibe-Trading/blob/80ffdda44c5c4db0dd84d70e051cca591cea67df/agent/src/scheduled_research/models.py#L433-L469","documentation":"Raised by Job from_dict (agent/src/scheduled_research/models.py:451) when 'consecutive_failures' is a bool, non-int, or negative. It counts successive failures for backoff/disable logic, so it must be a non-negative integer; bool is explicitly rejected because it subclasses int. Defaults to 0 when absent.","triggerScenarios":"{\"consecutive_failures\": -1} after resetting logic underflows, {\"consecutive_failures\": \"2\"} from stringified JSON, or {\"consecutive_failures\": True} from a flag mistakenly stored here.","commonSituations":"Retry bookkeeping that decrements below zero; JSON configs hand-edited with quoted numbers; storing a boolean 'failed' flag in the counter slot.","solutions":["Clamp counters: max(0, failures)","Keep counter arithmetic in int and avoid bools","Omit the field to default to 0"],"exampleFix":"// before\nrecord[\"consecutive_failures\"] = failures - 1\n\n// after\nrecord[\"consecutive_failures\"] = max(0, failures - 1)","handlingStrategy":"validation","validationCode":"def failures_ok(v):\n    return not isinstance(v, bool) and isinstance(v, int) and v >= 0","typeGuard":"def safe_failure_count(v, default=0):\n    if isinstance(v, bool) or not isinstance(v, int) or v < 0:\n        return default\n    return v","tryCatchPattern":"try:\n    job = Job.from_dict(record)\nexcept TypeError as exc:\n    if \"consecutive_failures\" in str(exc):\n        record = dict(record); record[\"consecutive_failures\"] = 0\n        job = Job.from_dict(record)\n    else:\n        raise","preventionTips":["Clamp failure counters with max(0, n)","Never let bools into numeric fields","Centralize failure bookkeeping in one helper"],"tags":["deserialization","type-error","failure-counter","scheduled-research"],"backgroundTag":"json-field-type-mismatch","analyzedSha":"80ffdda44c5c4db0dd84d70e051cca591cea67df","analyzedAt":"2026-08-28T12:46:38.989Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}