{"record":{"id":"adf75e45b58252c7","repo":"rohitg00/ai-engineering-from-scratch","slug":"batch-size-must-be-positive","errorCode":null,"errorMessage":"batch size must be positive","messagePattern":"batch size must be positive","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"certifications/claude/lessons/08-messages-api-and-application-lifecycle/code/main.py","lineNumber":180,"sourceCode":"    stopped = False\n    for event in events:\n        event_type = event.get(\"type\")\n        if stopped:\n            raise ProtocolError(\"event arrived after message_stop\")\n        if event_type == \"content_block_delta\":\n            delta = event.get(\"delta\", {})\n            if delta.get(\"type\") == \"text_delta\":\n                chunks.append(str(delta.get(\"text\", \"\")))\n        elif event_type == \"message_stop\":\n            stopped = True\n    if not stopped:\n        raise ProtocolError(\"stream ended without message_stop\")\n    return \"\".join(chunks)\n\n\ndef batch(items: list[Any], size: int) -> list[list[Any]]:\n    if size < 1:\n        raise ValueError(\"batch size must be positive\")\n    return [items[index : index + size] for index in range(0, len(items), size)]\n\n\ndef stable_cache_key(model: str, stable_prefix: str) -> str:\n    payload = f\"{model}\\0{stable_prefix}\".encode(\"utf-8\")\n    return hashlib.sha256(payload).hexdigest()\n\n\nIMAGE_MEDIA_TYPES = {\"image/jpeg\", \"image/png\", \"image/gif\", \"image/webp\"}\nDOCUMENT_MEDIA_TYPES = {\"application/pdf\", \"text/plain\"}\n\n\ndef build_multimodal_request(prompt: str, image_bytes: bytes, reusable_file_id: str) -> dict[str, Any]:\n    \"\"\"Build an offline request body with inline vision and a reusable file asset.\"\"\"\n    if not prompt.strip():\n        raise ValueError(\"prompt must not be empty\")\n    if not image_bytes:\n        raise ValueError(\"image_bytes must not be empty\")","sourceCodeStart":162,"sourceCodeEnd":198,"githubUrl":"https://github.com/rohitg00/ai-engineering-from-scratch/blob/39ea8a1c6d0b61f071226eff7ede4d4105fed820/certifications/claude/lessons/08-messages-api-and-application-lifecycle/code/main.py#L162-L198","documentation":"Raised by batch() when size is less than 1. The function slices items into fixed-size chunks via range(0, len(items), size); zero or negative sizes would silently produce wrong or empty results, so it fails fast instead (test_batch_and_cache_helpers_are_deterministic).","triggerScenarios":"Calling batch(items, 0), batch(items, -2), or passing a size computed from config/env that evaluates to 0.","commonSituations":"Batch size read from an env var or CLI flag that defaults to 0, integer division producing 0 for small inputs, or a size variable left uninitialized.","solutions":["Pass a positive integer size (typically 1-100)","Validate configurable batch sizes at startup and clamp or fail before the loop","Add a check where the size is computed, not where batch is called"],"exampleFix":"# before\nsize = total // count  # 0 when total < count\nbatch(items, size)\n# after\nsize = max(1, total // count)\nbatch(items, size)","handlingStrategy":"validation","validationCode":"if not isinstance(size, int) or size < 1:\n    raise ValueError(\"batch size must be a positive integer\")\nresult = batch(items, size)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Clamp configurable sizes with max(1, size) at the config boundary","Fail fast on unparsed env-var batch sizes instead of defaulting to 0"],"tags":["batching","argument-validation","python"],"backgroundTag":"invalid-batch-size","analyzedSha":"39ea8a1c6d0b61f071226eff7ede4d4105fed820","analyzedAt":"2026-08-26T03:13:46.626Z","schemaVersion":2},"datasetVersion":"2026-08-26T07:17:17.940Z"}