{"record":{"id":"563359d8eb3d96ec","repo":"apache/beam","slug":"num-splits-must-be-greater-than-or-equal-0","errorCode":null,"errorMessage":"num_splits must be greater than or equal 0","messagePattern":"num_splits must be greater than or equal 0","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/io/gcp/datastore/v1new/datastoreio.py","lineNumber":130,"sourceCode":"  def __init__(self, query, num_splits=0):\n    \"\"\"Initialize the `ReadFromDatastore` transform.\n\n    This transform outputs elements of type\n    :class:`~apache_beam.io.gcp.datastore.v1new.types.Entity`.\n\n    Args:\n      query: (:class:`~apache_beam.io.gcp.datastore.v1new.types.Query`) query\n        used to fetch entities.\n      num_splits: (:class:`int`) (optional) Number of splits for the query.\n    \"\"\"\n    super().__init__()\n\n    if not query.project:\n      raise ValueError(\"query.project cannot be empty\")\n    if not query:\n      raise ValueError(\"query cannot be empty\")\n    if num_splits < 0:\n      raise ValueError(\"num_splits must be greater than or equal 0\")\n\n    self._project = query.project\n    # using _namespace conflicts with DisplayData._namespace\n    self._datastore_namespace = query.namespace\n    self._query = query\n    self._num_splits = num_splits\n\n  def expand(self, pcoll):\n    # This is a composite transform involves the following:\n    #   1. Create a singleton of the user provided `query` and apply a ``ParDo``\n    #   that splits the query into `num_splits` queries if possible.\n    #\n    #   If the value of `num_splits` is 0, the number of splits will be\n    #   computed dynamically based on the size of the data for the `query`.\n    #\n    #   2. The resulting ``PCollection`` is sharded across workers using a\n    #   ``Reshuffle`` operation.\n    #","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/io/gcp/datastore/v1new/datastoreio.py#L112-L148","documentation":"ReadFromDatastore validates that num_splits is a non-negative integer; a negative value would request a nonsensical (impossible) number of query splits, so it is rejected before the split query is built.","triggerScenarios":"Calling ReadFromDatastore(query=q, num_splits=-1) or any negative int, typically from a miscomputed config value or an integer parse that produced a negative number.","commonSituations":"Autoscaling code computing splits as (target - actual) which can go negative; user config typo '-1'; uninitialized default of -1 used as a sentinel.","solutions":["Clamp the value: num_splits = max(0, num_splits) before constructing the transform.","Pass num_splits=None (or omit) to let the connector pick a sensible number of splits.","Fix the computation/config that produced the negative split count."],"exampleFix":"// before\nReadFromDatastore(query=q, num_splits=-1)  # ValueError\n\n// after\nReadFromDatastore(query=q, num_splits=max(0, desired_splits))","handlingStrategy":"validation","validationCode":"if num_splits is not None and num_splits < 0:\n    raise ValueError('num_splits must be >= 0')","typeGuard":"def is_valid_num_splits(n):\n    return n is None or (isinstance(n, int) and n >= 0)","tryCatchPattern":"try:\n    read = ReadFromDatastore(query=query, num_splits=n)\nexcept ValueError as e:\n    if 'num_splits' in str(e):\n        read = ReadFromDatastore(query=query)  # let library choose splits\n    else:\n        raise","preventionTips":["Clamp computed split counts with max(0, n).","Omit num_splits and let the connector decide when unsure.","Validate config values are non-negative ints at load time."],"tags":["python","apache-beam","datastore","gcp","argument-validation"],"backgroundTag":"value-out-of-range","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}