{"record":{"id":"02321212768bf930","repo":"HumanSignal/label-studio","slug":"error-validating-prediction-validation-errors","errorCode":null,"errorMessage":"Error validating prediction: {validation_errors}","messagePattern":"Error validating prediction: (.+?)","errorType":"validation","errorClass":"ValidationError","httpStatus":400,"severity":"error","filePath":"label_studio/tasks/serializers.py","lineNumber":118,"sourceCode":"        project = None\n        if 'task' in data:\n            project = data['task'].project\n        elif 'project' in data:\n            project = data['project']\n        ff_user = project.organization.created_by if project else 'auto'\n\n        # Only validate if we're updating the result field\n        if 'result' not in data:\n            return data\n\n        if not project:\n            raise ValidationError('Project is required for prediction validation')\n\n        custom_interface_validator = load_func(getattr(settings, 'CUSTOM_INTERFACE_PREDICTION_VALIDATOR', None))\n        if custom_interface_validator:\n            validation_errors = custom_interface_validator(project, data.get('result', []))\n            if validation_errors:\n                raise ValidationError(f'Error validating prediction: {validation_errors}')\n\n        if not flag_set('fflag_feat_utc_210_prediction_validation_15082025', user=ff_user):\n            # Skip validation if feature flag is not set\n            logger.info(f'Skipping prediction validation in PredictionSerializer for user {ff_user}')\n            return super().validate(data)\n\n        # Custom Interface projects normally keep the default <View></View>\n        # label_config and are validated above against output_schema instead.\n        if not project.label_config_is_not_default:\n            return super().validate(data)\n\n        # Validate prediction using LabelInterface\n        li = LabelInterface(project.label_config)\n        validation_errors = li.validate_prediction(data, return_errors=True)\n\n        if validation_errors:\n            raise ValidationError(f'Error validating prediction: {validation_errors}')\n","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/HumanSignal/label-studio/blob/0b49e9b53917880baf1dd85d574fe5541a9aafb2/label_studio/tasks/serializers.py#L100-L136","documentation":"PredictionSerializer.validate() supports a pluggable validator via settings.CUSTOM_INTERFACE_PREDICTION_VALIDATOR (loaded with load_func). When that setting is configured and the custom validator returns non-empty errors for the prediction's result, ValidationError wraps and re-raises those errors verbatim.","triggerScenarios":"Creating/updating a prediction whose result violates rules enforced by the custom validator configured in settings (e.g. organization-specific schema constraints on allowed labels/types), with the prediction-validation feature in play.","commonSituations":"Deployments with a custom validator from an older project config that rejects results from newly changed label configs; mismatches after editing the labeling interface; validator expecting a different result shape than the client sends.","solutions":["Read the returned validation_errors detail — it comes from your custom validator, so fix the result payload to satisfy it","Check the CUSTOM_INTERFACE_PREDICTION_VALIDATOR implementation in your settings to understand its rules","Update the custom validator if it is stale relative to the current label config","Temporarily remove/adjust the setting to confirm it is the source of the rejection"],"exampleFix":"// before\nCUSTOM_INTERFACE_PREDICTION_VALIDATOR = 'myapp.validators.old_pred_validator'\n// after\nCUSTOM_INTERFACE_PREDICTION_VALIDATOR = None  # or an updated validator matching current label config","handlingStrategy":"try-catch","validationCode":"from django.conf import settings\n\ndef precheck_with_custom_validator(project, result):\n    loader = getattr(settings, 'CUSTOM_INTERFACE_PREDICTION_VALIDATOR', None)\n    if not loader:\n        return\n    validator = __import__(loader.rsplit('.', 1)[0], fromlist=['x'])\n    fn = getattr(validator, loader.rsplit('.', 1)[1])\n    errors = fn(project, result)\n    if errors:\n        raise ValueError(f\"custom validator rejects result: {errors}\")","typeGuard":"def custom_validator_configured(settings) -> bool:\n    return bool(getattr(settings, 'CUSTOM_INTERFACE_PREDICTION_VALIDATOR', None))","tryCatchPattern":"from rest_framework.exceptions import ValidationError\ntry:\n    ser = PredictionSerializer(data=payload)\n    ser.is_valid(raise_exception=True)\nexcept ValidationError as e:\n    errors = e.detail\n    # detail is the custom validator's message — fix result per its rules\n    logger.error(\"custom prediction validation failed: %s\", errors)","preventionTips":["Keep the custom validator implementation in sync with current label configs","Read the wrapped validation_errors in the exception detail — they come from your own validator","Version the validator alongside label config changes","Test validator changes against representative prediction payloads before deploy"],"tags":["prediction","validation","custom-validator","settings"],"backgroundTag":"prediction-validation-failed","analyzedSha":"0b49e9b53917880baf1dd85d574fe5541a9aafb2","analyzedAt":"2026-08-29T00:39:52.578Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}