{"record":{"id":"3fb8b443eeb38d82","repo":"apache/beam","slug":"robustzscore-score-one-expected-univariate-input-but-got-s","errorCode":null,"errorMessage":"\"RobustZScore.score_one expected univariate input, but got %s\", str(x)","messagePattern":"\"RobustZScore\\.score_one expected univariate input, but got (.+?)\", str\\(x\\)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"sdks/python/apache_beam/ml/anomaly/detectors/robust_zscore.py","lineNumber":98,"sourceCode":"    if len(x.__dict__) != 1:\n      raise ValueError(\n          \"RobustZScore.learn_one expected univariate input, but got %s\",\n          str(x))\n\n    v = next(iter(x))\n    self._mad_tracker.push(v)\n\n  def score_one(self, x: beam.Row) -> Optional[float]:\n    \"\"\"Scores a data point using the Robust Z-Score.\n\n    Args:\n      x: A `beam.Row` containing a single numerical value.\n\n    Returns:\n      float | None: The Robust Z-Score.\n    \"\"\"\n    if len(x.__dict__) != 1:\n      raise ValueError(\n          \"RobustZScore.score_one expected univariate input, but got %s\",\n          str(x))\n\n    v = next(iter(x))\n    if v is None or math.isnan(v):\n      return None\n\n    median = self._mad_tracker.get_median()\n    mad = self._mad_tracker.get()\n\n    # not enough data points to compute median or median absolute deviation\n    if math.isnan(mad) or math.isnan(median):\n      return float('NaN')\n\n    if abs(mad) < EPSILON:\n      return 0.0\n\n    return abs(RobustZScore.SCALE_FACTOR * (v - median) / mad)","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/apache/beam/blob/12126d8942aaf848030c478b4c6a28c6af861c66/sdks/python/apache_beam/ml/anomaly/detectors/robust_zscore.py#L80-L116","documentation":"RobustZScore.score_one computes the robust z-score of a single point and requires a univariate beam.Row with exactly one field. Rows with a different field count raise ValueError. The score math (x - median) / MAD only applies to one value per point.","triggerScenarios":"Scoring beam.Row objects with multiple attributes or empty rows through RobustZScore.score_one, often when the scored PCollection still contains metadata columns.","commonSituations":"Joining/enriching rows before scoring so they gain extra fields; passing raw source rows; inconsistent shaping between the learn and score stages of the pipeline.","solutions":["Project the input to a single numeric field before scoring.","Ensure the score-stage Row shape matches the learn-stage Row shape.","Add a pre-scoring validation step that rejects or logs multi-field rows."],"exampleFix":"// before\nenriched_rows | beam.Map(detector.score_one)\n// after\nenriched_rows | beam.Map(lambda r: beam.Row(value=r.metric)) | beam.Map(detector.score_one)","handlingStrategy":"validation","validationCode":"if len(row.__dict__) != 1:\n    raise ValueError(f\"RobustZScore.score_one requires exactly one field, got: {list(row.__dict__)}\")","typeGuard":"def is_univariate(row): return len(getattr(row, '__dict__', {})) == 1","tryCatchPattern":"try:\n    score = detector.score_one(row)\nexcept ValueError as e:\n    score = None\n    logger.warning(\"unscorable row %r: %s\", row, e)","preventionTips":["Score only the projected single-field Row","Avoid enrichment between learn and score without re-projecting","Validate schema after windowing/joins"],"tags":["python","anomaly-detection","validation","univariate"],"backgroundTag":"invalid-argument-value","analyzedSha":"12126d8942aaf848030c478b4c6a28c6af861c66","analyzedAt":"2026-09-13T01:50:10.254Z","contentChangedAt":"2026-09-13T01:50:10.254Z","schemaVersion":2},"datasetVersion":"2026-09-20T03:17:13.778Z"}