Skip to content

Code validation repeats the first result for subsequent target columns #1092

Closed

Description

@nabinchha

Priority Level

Medium (Annoying but has workaround)

Describe the bug

When a code-validation column has multiple target_columns, the first target receives its per-row results correctly, but every later target repeats its first validation result across all rows.

This can silently mark invalid code as valid when the first record passes, or mark valid code as invalid when the first record fails. Validation metadata is repeated along with is_valid.

Steps/Code to reproduce bug

Run this in a DataDesigner development environment. It uses the real Python validator/Ruff; only the resource provider is stubbed. No model endpoint or credentials are required.

from __future__ import annotations

from unittest.mock import Mock

import pandas as pd

from data_designer.config.column_configs import ValidationColumnConfig
from data_designer.config.run_config import RunConfig
from data_designer.config.utils.code_lang import CodeLang
from data_designer.config.validator_params import CodeValidatorParams, ValidatorType
from data_designer.engine.column_generators.generators.validation import ValidationColumnGenerator
from data_designer.engine.resources.resource_provider import ResourceProvider

provider = Mock(spec=ResourceProvider)
provider.run_config = RunConfig()

data = pd.DataFrame(
    {
        "first_code": ["a = 1", "b = 2", "c = 3"],
        "second_code": ["a = 1", "def f(:", "c = 3"],
    }
)
config = ValidationColumnConfig(
    name="check",
    validator_type=ValidatorType.CODE,
    validator_params=CodeValidatorParams(code_lang=CodeLang.PYTHON),
    target_columns=["first_code", "second_code"],
    batch_size=2,
)
result = ValidationColumnGenerator(config=config, resource_provider=provider).generate(data)
actual = [row["second_code"]["is_valid"] for row in result["check"]]
print(actual)
assert actual == [True, False, True]

Actual output: [True, True, True]; the assertion fails.

As a control, change target_columns to ["second_code"] and read row["is_valid"] instead. That correctly returns [True, False, True]. Both the failure and control reproduce with batch_size=2 and batch_size=10.

If a restricted environment prevents Ruff cache writes, run with RUFF_NO_CACHE=true.

Expected behavior

Each target column's nested validation result should correspond to that target's code in the same input row. The second target above should return [True, False, True], including a failed validation and the matching diagnostics for the syntax-error row.

Agent Diagnostic / Prior Investigation

The shared Python/SQL result-assembly branch in validation.py uses:

for dict_output in outputs_as_dicts:
    dict_output[cols[0]] = concatenated_outputs[0].model_dump(mode="json")

For targets after the first, this always selects output zero instead of pairing each row with its corresponding result.

Suggested fix: check each target's output count, then pair output dictionaries with the corresponding validation outputs instead of selecting concatenated_outputs[0]. Add Python/SQL regression coverage with multiple targets and different validity/metadata between rows.

Additional context

Workaround: define a separate validation column for each code target, with one target_columns entry per validation column.

AI assistance: Codex investigated the bug, executed the reproduction and controls, and prepared this report.

Checklist

  • I reproduced this issue or provided a minimal example
  • I searched the docs/issues myself, or had my agent do so
  • If I used an agent, I included its diagnostics above

Activity

  1. edjson commented on Oct 28, 2026

    @edjson
    Contributor

    Hello, from my understanding of the bug, when a validation column has more than one code target, the first target gets its own result in each row, but every target after it gets the first rows result in all rows. I believe the issue is from the line dict_output[cols[0]] = concatenated_outputs[0].model_dump(mode="json") in validation.py, because concatenated_outputs[0].model_dump(mode="json") never updates in the loop, so dict_output[cols[0]] will always be assigned row 0's result.

    I would like to work on this following the "suggested fix". The fix would just be replacing the loop with:

    for dict_output, output in zip(outputs_as_dicts, concatenated_outputs, strict=True):
        dict_output[cols[0]] = output.model_dump(mode="json")

    That way it maps one to one, and can correctly add to the rows that already exist. The tradeoff is that a mismatch raises a generic ValueError ("zip() argument 2 is shorter than argument 1") with no column name, but _validate_batch already raises an error if any batch returns the wrong number of results, and the parallel path pads failed batches to the right size.

    I would also add Python and SQL regression tests with multiple targets. If I am able could this get the triaged label so I can open the PR?

  2. added
    triagedIssue reviewed and approved by a maintainer
    on Oct 28, 2026
  3. nabinchha commented on Oct 28, 2026

    @nabinchha
    ContributorAuthor

    @edjson Thanks for taking this issue on! Your diagnosis and the proposed zip(..., strict=True) fix are correct. Please go ahead with the PR and the Python/SQL regression tests.

    One small clarification: failed-batch padding in the parallel path applies only to remote validators; multi-target Python/SQL validation runs sequentially. The per-batch result-count check in _validate_batch on current main already covers that sequential path, so the generic ValueError from strict zip would be a defensive fallback.

    For the regression tests, please assert both mixed validity and row-specific metadata for subsequent targets, with single-batch and multiple-batch coverage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingtriagedIssue reviewed and approved by a maintainer

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions