Error Handling
Complete reference for SDK errors and how to resolve them.
Error Categories
SDK errors occur at two stages:
- Build-Time Errors - When calling
.build()on builders - Validation Errors - When calling
validate()on agent definitions
Build-Time Errors
These errors are raised immediately when using builders incorrectly.
MissingFieldError
Required field not provided before calling .build().
Example:
# Error: Missing version and description
agent = AgentBuilder("my-agent").build()Error Message:
MissingFieldError: Required field 'version' is missing or empty in AgentBuilderFix:
agent = (
AgentBuilder("my-agent")
.version("1.0.0") # Required
.description("Agent") # Required
.add_workflow(workflow) # Required
.build()
)Required Fields by Builder:
| Builder | Required Fields |
|---|---|
| AgentBuilder | name, version, description, workflows (at least one) |
| WorkflowBuilder | name, description, nodes (at least one), entry_node |
| NodeBuilder | name, description, resource reference (uses_*) |
| PromptBuilder | name, description, template |
| ToolBuilder | name, description |
| ModuleBuilder | name, description |
InvalidFormatError
Value doesn’t match required format.
Common Cases:
Invalid Name Format
# Error: Name contains uppercase and space
agent = AgentBuilder("My Agent").build()Error Message:
InvalidFormatError: Invalid value for 'name': 'My Agent'.
Agent name must start with lowercase letter and contain only lowercase letters, numbers, and hyphensName Format Rules:
- Pattern:
[a-z][a-z0-9-]* - Must start with lowercase letter
- Can contain: lowercase letters, numbers, hyphens
- Cannot contain: uppercase, spaces, underscores, special characters
Valid Names:
"my-agent" # Valid
"data-processor" # Valid
"agent-v2" # Valid
"task123" # ValidInvalid Names:
"MyAgent" # Uppercase (invalid)
"my_agent" # Underscore (invalid)
"123agent" # Starts with number (invalid)
"my agent" # Space (invalid)
"my@agent" # Special character (invalid)Fix:
agent = AgentBuilder("my-agent").version("1.0.0")...build()Invalid Version Format
# Error: Not semantic versioning
agent = AgentBuilder("my-agent").version("v1").build()Error Message:
InvalidFormatError: Invalid value for 'version': 'v1'.
Version must follow semantic versioning format (e.g., '1.0.0')Version Format Rules:
- Pattern:
MAJOR.MINOR.PATCH - Example:
1.0.0,2.3.1,0.1.0
Fix:
agent = AgentBuilder("my-agent").version("1.0.0")...build()DuplicateError
Name used multiple times in same scope.
Example:
# Error: Two nodes named "task"
workflow = (
WorkflowBuilder("main")
.add_node(NodeBuilder("task").description("Task 1").uses_prompt("p1").build())
.add_node(NodeBuilder("task").description("Task 2").uses_prompt("p2").build())
.build()
)Error Message:
DuplicateError: Duplicate node name 'task' in workflow 'main'.
Each node must have a unique name within its scope.Scoping Rules:
| Scope | Must Be Unique Within |
|---|---|
| Nodes | Same workflow |
| Workflows | Same agent |
| Prompts | Same agent |
| Tools | Same agent |
| Modules | Same agent |
Fix:
workflow = (
WorkflowBuilder("main")
.add_node(NodeBuilder("task-1").description("Task 1").uses_prompt("p1").build())
.add_node(NodeBuilder("task-2").description("Task 2").uses_prompt("p2").build())
.build()
)ReferenceError
Node references undefined resource.
Example:
# Error: Prompt "my-prompt" not defined in agent
agent = (
AgentBuilder("test")
.version("1.0.0")
.description("Test")
.add_workflow(
WorkflowBuilder("main")
.add_node(
NodeBuilder("task")
.description("Task")
.uses_prompt("my-prompt") # Not defined!
.build()
)
.entry_node("task")
.build()
)
.build()
)Error Message:
ReferenceError: 'task' references undefined prompt 'my-prompt'.
The prompt must be defined in the agent.Fix:
# Define the prompt first
prompt = (
PromptBuilder("my-prompt")
.description("My prompt")
.template("Do task")
.build()
)
agent = (
AgentBuilder("test")
.version("1.0.0")
.description("Test")
.add_prompt(prompt) # Add the prompt
.add_workflow(...)
.build()
)Resource Types:
uses_module(name)→ must match a module in agent.modulesuses_prompt(name)→ must match a prompt in agent.promptsuses_tool(name)→ must match a tool in agent.tools
EmptyCollectionError
Required collection is empty.
Example:
# Error: No workflows added
agent = (
AgentBuilder("test")
.version("1.0.0")
.description("Test")
.build()
)Error Message:
EmptyCollectionError: 'Agent 'test'' has no workflows.
At least one workflow is required.Fix:
agent = (
AgentBuilder("test")
.version("1.0.0")
.description("Test")
.add_workflow(workflow) # Add at least one
.build()
)Minimum Requirements:
| Collection | Minimum Count |
|---|---|
| Agent workflows | 1 |
| Workflow nodes | 1 |
Validation Errors
These errors occur during validate() call.
CyclicDependencyError
Workflow contains cycle (not a DAG).
Example:
# Error: node-a → node-b → node-c → node-a (cycle!)
workflow = (
WorkflowBuilder("main")
.add_node(
NodeBuilder("node-a")
.description("Node A")
.uses_prompt("p")
.next_nodes("node-b")
.build()
)
.add_node(
NodeBuilder("node-b")
.description("Node B")
.uses_prompt("p")
.next_nodes("node-c")
.build()
)
.add_node(
NodeBuilder("node-c")
.description("Node C")
.uses_prompt("p")
.next_nodes("node-a") # Creates cycle!
.build()
)
.entry_node("node-a")
.build()
)
result = validate(agent)
# CyclicDependencyError in result.errorsError Message:
CyclicDependencyError: Workflow contains a cycle: node-a → node-b → node-c → node-aFix: Remove the cycle by restructuring workflow as a DAG:
workflow = (
WorkflowBuilder("main")
.add_node(
NodeBuilder("node-a")
.description("Node A")
.uses_prompt("p")
.next_nodes("node-b")
.build()
)
.add_node(
NodeBuilder("node-b")
.description("Node B")
.uses_prompt("p")
.next_nodes("node-c")
.build()
)
.add_node(
NodeBuilder("node-c")
.description("Node C")
.uses_prompt("p")
.build() # No cycle
)
.entry_node("node-a")
.build()
)UnreachableNodeError
Node cannot be reached from entry node.
Example:
# Error: orphan-node has no incoming edges
workflow = (
WorkflowBuilder("main")
.add_node(
NodeBuilder("entry")
.description("Entry")
.uses_prompt("p")
.next_nodes("task-a")
.build()
)
.add_node(
NodeBuilder("task-a")
.description("Task A")
.uses_prompt("p")
.build()
)
.add_node(
NodeBuilder("orphan-node") # Not connected!
.description("Orphan")
.uses_prompt("p")
.build()
)
.entry_node("entry")
.build()
)
result = validate(agent)
# UnreachableNodeError in result.errorsError Message:
UnreachableNodeError: Node 'orphan-node' is unreachable from entry node 'entry'.
All nodes must be reachable via edges.Fix Option 1: Connect the node to workflow:
.add_node(
NodeBuilder("entry")
.next_nodes("task-a", "orphan-node") # Add connection
.build()
)Fix Option 2: Remove unused node:
# Simply don't add orphan-nodeError Handling Patterns
Basic Try-Catch
from ainalyn.domain.errors import DomainError
try:
agent = (
AgentBuilder("my-agent")
.version("1.0.0")
.description("Agent")
.add_workflow(workflow)
.build()
)
except DomainError as e:
print(f"Build error: {e.message}")Specific Error Handling
from ainalyn.domain.errors import (
MissingFieldError,
InvalidFormatError,
ReferenceError,
DuplicateError,
EmptyCollectionError,
)
try:
agent = AgentBuilder("test").build()
except MissingFieldError as e:
print(f"Missing required field: {e.field_name}")
print(f"In: {e.entity_type}")
except InvalidFormatError as e:
print(f"Invalid {e.field_name}: {e.value}")
print(f"Constraint: {e.constraint}")
except ReferenceError as e:
print(f"'{e.source}' references undefined {e.resource_type}: {e.reference}")
except DuplicateError as e:
print(f"Duplicate {e.entity_type} name: {e.name}")
if e.scope:
print(f"In: {e.scope}")
except EmptyCollectionError as e:
print(f"{e.parent_name} has no {e.collection_name}")Validation Result Handling
from ainalyn.api import validate
result = validate(agent)
# Check if valid
if not result.is_valid:
print("Validation failed:")
for error in result.errors:
print(f" [{error.severity}] {error.code}")
print(f" {error.message}")
if error.location:
print(f" Location: {error.location}")
# Check for warnings
if result.has_warnings:
print("\nWarnings:")
for error in result.errors:
if error.severity == Severity.WARNING:
print(f" {error.code}: {error.message}")Compilation Error Handling
from ainalyn.api import compile_agent
from pathlib import Path
result = compile_agent(agent, Path("agent.yaml"))
if result.is_successful:
print(f"Success! Compiled to {result.output_path}")
else:
print("Compilation failed:")
for error in result.validation_result.errors:
print(f"\n{error.code}")
print(f" {error.message}")Error Code Reference
Build-Time Error Codes
| Code | Exception | Severity | Description |
|---|---|---|---|
| MISSING_FIELD | MissingFieldError | ERROR | Required field not provided |
| INVALID_FORMAT | InvalidFormatError | ERROR | Value doesn’t match format |
| DUPLICATE_NAME | DuplicateError | ERROR | Name collision in scope |
| REFERENCE_ERROR | ReferenceError | ERROR | Undefined resource reference |
| EMPTY_COLLECTION | EmptyCollectionError | ERROR | Required collection empty |
Validation Error Codes
| Code | Exception | Severity | Description |
|---|---|---|---|
| CYCLIC_DEPENDENCY | CyclicDependencyError | ERROR | Workflow contains cycle |
| UNREACHABLE_NODE | UnreachableNodeError | ERROR | Node unreachable from entry |
| ORPHANED_RESOURCE | N/A | WARNING | Resource defined but unused |
Best Practices
1. Build Incrementally
Catch errors early by validating after each step:
# Build nodes
node1 = NodeBuilder("task1").description("Task 1").uses_prompt("p1").build()
node2 = NodeBuilder("task2").description("Task 2").uses_prompt("p2").build()
# Build workflow
workflow = (
WorkflowBuilder("main")
.description("Main")
.add_node(node1)
.add_node(node2)
.entry_node("task1")
.build()
)
# Build agent
agent = (
AgentBuilder("my-agent")
.version("1.0.0")
.description("Agent")
.add_prompt(prompt1)
.add_prompt(prompt2)
.add_workflow(workflow)
.build()
)
# Validate
result = validate(agent)
assert result.is_valid2. Use compile_agent()
Combines validation and export:
from ainalyn.api import compile_agent
from pathlib import Path
result = compile_agent(agent, Path("agent.yaml"))
if result.is_successful:
# Safe to submit
print("Ready for submission")
else:
# Fix errors first
for error in result.validation_result.errors:
print(error.message)3. Handle Errors Specifically
Catch specific error types for targeted fixes:
try:
agent = builder.build()
except MissingFieldError as e:
# Add missing field
pass
except InvalidFormatError as e:
# Fix format
pass
except ReferenceError as e:
# Define resource
pass4. Review Error Messages
Error messages include helpful information:
- Field name that caused error
- Invalid value
- Constraint that was violated
- Location in definition
except InvalidFormatError as e:
print(f"Field: {e.field_name}")
print(f"Value: {e.value}")
print(f"Rule: {e.constraint}")Quick Reference Table
| Error | Common Cause | Quick Fix |
|---|---|---|
| MissingFieldError | Forgot .version() or .description() | Add required method call |
| InvalidFormatError (name) | Used uppercase or spaces in name | Use lowercase with hyphens |
| InvalidFormatError (version) | Version not “X.Y.Z” format | Use semantic versioning |
| DuplicateError | Two nodes with same name | Rename one node |
| ReferenceError | Node uses undefined prompt | Add prompt to agent |
| EmptyCollectionError | No workflows in agent | Add at least one workflow |
| CyclicDependencyError | Circular workflow | Remove cycle |
| UnreachableNodeError | Disconnected node | Connect or remove node |
Getting Help
If you encounter an error not covered here:
- Read the full error message carefully
- Check API Reference for detailed error information
- Review Validation Guide for validation rules
- Consult How the SDK Works for compilation process
- Report issues on GitHub