Skip to content

Commit 045530a

Browse files
committed
fix: handle corrupted unrendered template data gracefully
- Wrap UnrenderedTemplateControl.model_validate in _parse_stored_control_data with proper error handling (returns 422 CORRUPTED_DATA instead of 500) - Wrap unrendered parse in list_controls_for_agent with try/except to skip corrupted rows instead of crashing the entire listing - Remove redundant unused-parameter check in render_template_control_input (already caught by validate_template_structure called at the top)
1 parent 5b0a530 commit 045530a

3 files changed

Lines changed: 34 additions & 21 deletions

File tree

‎server/src/agent_control_server/endpoints/controls.py‎

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,24 @@ def _parse_stored_control_data(
109109
) -> ControlDefinition | UnrenderedTemplateControl:
110110
"""Parse stored JSONB into the appropriate model type."""
111111
if _is_unrendered_template(data):
112-
return UnrenderedTemplateControl.model_validate(data)
112+
try:
113+
return UnrenderedTemplateControl.model_validate(data)
114+
except ValidationError:
115+
raise APIValidationError(
116+
error_code=ErrorCode.CORRUPTED_DATA,
117+
detail=f"Control '{control_name}' has corrupted unrendered template data",
118+
resource="Control",
119+
resource_id=str(control_id),
120+
hint=f"Update the control data using PUT /api/v1/controls/{control_id}/data.",
121+
errors=[
122+
ValidationErrorItem(
123+
resource="Control",
124+
field="data",
125+
code="corrupted_data",
126+
message="Stored unrendered template data is invalid.",
127+
)
128+
],
129+
)
113130

114131
return parse_control_definition_or_api_error(
115132
data,

‎server/src/agent_control_server/services/control_templates.py‎

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -759,25 +759,9 @@ def render_template_control_input(
759759
template=template,
760760
)
761761

762-
unused_parameters = sorted(set(template.parameters) - referenced_parameters)
763-
if unused_parameters:
764-
raise APIValidationError(
765-
error_code=ErrorCode.TEMPLATE_RENDER_ERROR,
766-
detail="Template defines parameters that are never referenced",
767-
resource="Control",
768-
hint="Remove unused parameters or reference them in definition_template.",
769-
errors=[
770-
ValidationErrorItem(
771-
resource="Control",
772-
field=f"template.parameters.{parameter_name}",
773-
code="unused_template_parameter",
774-
message=f"Template parameter '{parameter_name}' is never referenced.",
775-
parameter=parameter_name,
776-
parameter_label=template.parameters[parameter_name].label,
777-
)
778-
for parameter_name in unused_parameters
779-
],
780-
)
762+
# Note: unused-parameter detection is handled by validate_template_structure
763+
# (called above). The referenced_parameters set is still tracked here for
764+
# the reverse path map used in error remapping.
781765

782766
try:
783767
rendered_control = ControlDefinition.model_validate(rendered_payload)

‎server/src/agent_control_server/services/controls.py‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,15 @@
88
from sqlalchemy import select, union
99
from sqlalchemy.ext.asyncio import AsyncSession
1010

11+
from ..logging_utils import get_logger
1112
from ..models import Control, agent_controls, agent_policies, policy_controls
1213
from .control_definitions import (
1314
parse_control_definition_or_api_error,
1415
parse_runtime_control_definition_or_api_error,
1516
)
1617

18+
_logger = get_logger(__name__)
19+
1720

1821
@dataclass(frozen=True)
1922
class RuntimeControl:
@@ -89,7 +92,16 @@ async def list_controls_for_agent(
8992
and c.data.get("template") is not None
9093
and c.data.get("condition") is None
9194
):
92-
unrendered = UnrenderedTemplateControl.model_validate(c.data)
95+
try:
96+
unrendered = UnrenderedTemplateControl.model_validate(c.data)
97+
except Exception:
98+
_logger.warning(
99+
"Skipping control '%s' (id=%s): corrupted unrendered template data",
100+
c.name,
101+
c.id,
102+
exc_info=True,
103+
)
104+
continue
93105
api_controls.append(APIControl(id=c.id, name=c.name, control=unrendered))
94106
continue
95107

0 commit comments

Comments
 (0)