Skip to content

Validation System

Moira performs comprehensive validation at multiple levels to ensure workflow integrity and correct agent responses.

Workflows are validated against a JSON Schema definition:

  • Structure validation against workflow schema
  • Required field checking (id, metadata, nodes)
  • Type validation for all properties
  • Enum validation for node types

Graph structure is analyzed for correctness:

  • Node connectivity - All connections point to valid nodes
  • Required nodes - Start node must exist
  • Circular dependencies - Loops are detected and flagged
  • Unreachable nodes - Nodes not connected from start
  • Registry entries - Each variableRegistry entry must be a valid JSON Schema (a malformed items/pattern/etc. is a blocking error) with a non-empty description

Agent responses are validated against inputSchema:

  • AJV-based JSON Schema validation
  • Type checking for response fields
  • Required field validation
  • Pattern and format validation

Validation returns structured results:

{
valid: boolean;
errors: ValidationError[];
warnings: ValidationWarning[];
}
TypeDescriptionExample
schemaJSON structure invalidMissing required field
structureGraph structure invalidOrphan node
connectionConnection invalidPoints to non-existent node
referenceReference invalidInvalid subgraph ID
TypeDescriptionThreshold
performanceLarge workflow>20 agent-directive nodes
complexityComplex conditionsDeeply nested conditions
contextLarge context>100KB context size
{
"id": "valid-workflow",
"metadata": {
"name": "Valid Workflow",
"version": "1.0.0",
"description": "A valid workflow"
},
"nodes": [
{ "id": "start", "type": "start", "connections": { "default": "task" } },
{
"id": "task",
"type": "agent-directive",
"directive": "...",
"completionCondition": "...",
"connections": { "success": "end" }
},
{ "id": "end", "type": "end" }
]
}

Result:

{ "valid": true, "errors": [], "warnings": [] }
{
"nodes": [
{ "id": "start", "type": "start", "connections": { "default": "missing" } },
{ "id": "end", "type": "end" }
]
}

Result:

{
"valid": false,
"errors": [
{
"type": "connection",
"message": "Node 'start' references non-existent node 'missing'",
"nodeId": "start"
}
]
}

Large workflow triggers performance warning:

{
"valid": true,
"errors": [],
"warnings": [
{
"type": "performance",
"message": "Workflow has 25 agent-directive nodes. Consider breaking into subgraphs.",
"count": 25
}
]
}

Agent responses are validated against inputSchema defined on agent-directive nodes.

Nodes without inputSchema require empty input from agent. Non-empty responses are rejected:

// Node without inputSchema
{ "id": "task", "type": "agent-directive", "directive": "..." }
// Valid: empty response
{}
// Invalid: non-empty response
{ "result": "done" } // Rejected with validation error
{
"type": "agent-directive",
"inputSchema": {
"type": "object",
"properties": {
"result": { "type": "string" },
"confidence": { "type": "number", "minimum": 0, "maximum": 10 }
},
"required": ["result"]
}
}
{ "result": "completed", "confidence": 8 }
{ "confidence": "high" }

Error:

{
"valid": false,
"errors": [
{ "field": "result", "message": "Required field missing" },
{ "field": "confidence", "message": "Expected number, got string" }
]
}

The validator emits a warning (severity warning, not an error — the workflow is still valid) when a variableRegistry variable is referenced in a directive, completionCondition, message, or condition, but the variable has no default and is never written by any upstream node’s globalInputs (and is not present in the start node’s initialData).

At runtime, such a reference renders the literal placeholder [[UNDEFINED_VARIABLE]] instead of a value.

{
"valid": true,
"errors": [],
"warnings": [
{
"type": "structure",
"severity": "warning",
"nodeId": "do-work",
"message": "Variable 'iteration' is referenced in node 'do-work' but has no default and is never written by an upstream node. It will render [[UNDEFINED_VARIABLE]] at runtime."
}
]
}

Fix it one of two ways:

  • Add a default to the variable in variableRegistry.
  • Have an upstream node write the variable via its globalInputs before the node that references it.

Substituted variable and data VALUES are never re-executed as templates. When the engine interpolates a value into a directive or message, that value is treated as a literal string — brace syntax originating from substituted data is neutralized and not re-parsed.

This means templates only ever execute in author-controlled static node fields, not in values that arrive from agent input or external data.

  1. Always include inputSchema - Validate agent responses for consistent data
  2. Keep workflows focused - Split large workflows into subgraphs
  3. Test validation - Use manage with includeValidation: true
  4. Handle errors gracefully - Define error connections for validation failures