Troubleshooting Hooks
This guide helps you diagnose and fix common issues when working with hooks in Radium.
Hook Not Executingβ
Symptomsβ
- Hook is registered but never executes
- No logs or output from hook
- Hook appears in
rad hooks listbut doesn't run
Possible Causes and Solutionsβ
Hook is Disabledβ
Check:
rad hooks info my-hook
Solution:
rad hooks enable my-hook
Or in code:
registry.set_enabled("my-hook", true).await?;
Wrong Hook Typeβ
Check: Verify the hook type matches the execution point.
Solution: Ensure hook type matches:
BeforeModelhooks only execute before model callsBeforeToolhooks only execute before tool execution- etc.
Hook Not Registeredβ
Check: Verify hook is registered:
let hooks = registry.get_hooks(HookType::BeforeModel).await;
assert!(hooks.iter().any(|h| h.name() == "my-hook"));
Solution: Register the hook:
registry.register(hook).await?;
Priority Too Lowβ
Check: If other hooks stop execution (should_continue = false), lower priority hooks won't run.
Solution: Increase hook priority:
let hook = MyHook::new("my-hook", 250); // Higher priority
Priority Issuesβ
Symptomsβ
- Hooks execute in wrong order
- Important hooks run after less important ones
Solutionβ
Hooks execute in priority order (higher priority first). Check priorities:
let hooks = registry.get_hooks(HookType::BeforeModel).await;
for hook in hooks {
println!("{}: priority {}", hook.name(), hook.priority().value());
}
Guidelines:
- Security/validation: 200+
- Standard operations: 100-199
- Optional/monitoring: <100
Context Serialization Errorsβ
Symptomsβ
Serializationerrors when executing hooksInvalidConfigerrors- Data not accessible in context
Possible Causes and Solutionsβ
Invalid JSON in Contextβ
Error:
Serialization error: invalid JSON
Solution: Ensure context data is valid JSON:
let context = HookContext::new(
"before_model",
json!({
"input": "valid string",
"count": 42,
}),
);
Type Mismatchβ
Error:
Failed to get field: type mismatch
Solution: Check types when accessing context:
// Wrong
let count = context.data.get("count").unwrap().as_u64().unwrap();
// Right - handle Option
if let Some(count) = context.data.get("count").and_then(|v| v.as_u64()) {
// Use count
}
Missing Fieldsβ
Error:
Field 'input' not found
Solution: Check for field existence:
if let Some(input) = context.data.get("input").and_then(|v| v.as_str()) {
// Use input
} else {
// Handle missing field
}
Performance Problemsβ
Symptomsβ
- Slow execution
- High latency
- Timeouts
Possible Causes and Solutionsβ
Blocking Operationsβ
Problem: Using blocking I/O in async hooks.
Solution: Use async operations:
// Bad
std::fs::read("file.txt")?;
// Good
tokio::fs::read("file.txt").await?;
Expensive Operationsβ
Problem: Performing expensive computations in hooks.
Solution: Cache results or move to background:
// Cache expensive operations
if let Some(cached) = self.cache.get(&key).await {
return Ok(HookExecutionResult::with_data(cached));
}
let result = expensive_operation().await?;
self.cache.set(&key, result.clone()).await;
Too Many Hooksβ
Problem: Registering too many hooks slows execution.
Solution:
- Disable unused hooks
- Combine related hooks
- Use lower priority for non-critical hooks
Thread Safety Issuesβ
Symptomsβ
- Race conditions
- Data corruption
- Panics in concurrent execution
Possible Causes and Solutionsβ
Unsynchronized Shared Stateβ
Problem: Modifying shared state without locks.
Solution: Use Arc<RwLock<>>:
// Bad
pub struct UnsafeHook {
counter: u64, // Not thread-safe!
}
// Good
pub struct SafeHook {
counter: Arc<RwLock<u64>>, // Thread-safe
}
async fn execute(&self, _context: &HookContext) -> Result<HookExecutionResult> {
let mut counter = self.counter.write().await;
*counter += 1;
Ok(HookExecutionResult::success())
}
Deadlocksβ
Problem: Holding locks too long or acquiring multiple locks.
Solution:
- Keep lock scope minimal
- Acquire locks in consistent order
- Use
try_lockwhen appropriate
Error Handling Issuesβ
Symptomsβ
- Hooks crash on errors
- Errors not logged
- Execution stops unexpectedly
Possible Causes and Solutionsβ
Unhandled Errorsβ
Problem: Not handling errors in hooks.
Solution: Always handle errors:
async fn execute(&self, context: &HookContext) -> Result<HookExecutionResult> {
match self.process(context).await {
Ok(_) => Ok(HookExecutionResult::success()),
Err(e) => {
tracing::warn!(error = %e, "Hook processing failed");
Ok(HookExecutionResult::error(e.to_string()))
}
}
}
Panicking on Errorβ
Problem: Using unwrap() or panicking.
Solution: Use proper error handling:
// Bad
let value = context.data.get("key").unwrap().as_str().unwrap();
// Good
if let Some(value) = context.data.get("key").and_then(|v| v.as_str()) {
// Use value
} else {
return Ok(HookExecutionResult::error("Missing key"));
}
Configuration Issuesβ
Symptomsβ
- Configuration not loading
- Hooks not enabled from config
- Invalid configuration errors
Possible Causes and Solutionsβ
Invalid TOMLβ
Error:
ConfigParse error: invalid TOML
Solution: Validate TOML syntax:
[[hooks]]
name = "my-hook"
type = "before_model"
priority = 100
enabled = true
Missing Required Fieldsβ
Error:
InvalidConfig: missing required field 'name'
Solution: Ensure all required fields are present:
name: Hook nametype: Hook typepriority: Priority (optional, defaults to 100)enabled: Enable state (optional, defaults to true)
Configuration Not Loadedβ
Problem: Configuration file exists but hooks not configured.
Solution: Load configuration:
HookLoader::load_from_workspace(workspace_root, ®istry).await?;
Integration Issuesβ
Symptomsβ
- Hooks not working with orchestrator
- Integration errors
- Missing dependencies
Possible Causes and Solutionsβ
Missing Feature Flagβ
Error:
error: cannot find type `OrchestratorHooks` in module `hooks`
Solution: Enable orchestrator-integration feature:
[dependencies]
radium-core = { path = "../../crates/radium-core", features = ["orchestrator-integration"] }
Circular Dependencyβ
Error:
circular dependency detected
Solution: Use feature flags to break circular dependencies (already handled in core).
Debugging Tipsβ
Enable Debug Loggingβ
RUST_LOG=radium_core::hooks=debug rad <command>
Check Hook Registrationβ
let hooks = registry.get_hooks(HookType::BeforeModel).await;
for hook in hooks {
println!("Registered: {} (priority: {})", hook.name(), hook.priority().value());
}
Verify Hook Executionβ
Add logging to hooks:
async fn execute(&self, context: &HookContext) -> Result<HookExecutionResult> {
tracing::debug!(hook = %self.name, "Hook executing");
// ... hook logic
tracing::debug!(hook = %self.name, "Hook completed");
Ok(HookExecutionResult::success())
}
Test Hook in Isolationβ
#[tokio::test]
async fn test_hook() {
let hook = MyHook::new("test", 100);
let context = create_test_context();
let result = hook.execute(&context).await.unwrap();
assert!(result.success);
}
Common Error Messagesβ
"Hook not found"β
Cause: Hook not registered or wrong name.
Solution: Register hook or check name:
registry.register(hook).await?;
"Hook execution failed"β
Cause: Hook returned an error.
Solution: Check hook implementation and error handling.
"Invalid hook configuration"β
Cause: Configuration file has invalid syntax or missing fields.
Solution: Validate configuration file syntax and required fields.
"Rate limit exceeded"β
Cause: Too many calls in time window (if using rate limit hook).
Solution: Adjust rate limit configuration or wait for window to reset.
Getting Helpβ
If you're still experiencing issues:
- Check the API Reference for correct usage
- Review Best Practices for common patterns
- Look at Examples for working code
- Enable debug logging to see detailed execution flow
- Test hooks in isolation to identify the issue
Summaryβ
- Hook not executing: Check enable status, registration, hook type, priority
- Priority issues: Verify priorities and execution order
- Serialization errors: Check JSON validity and type handling
- Performance problems: Use async operations, cache results, reduce hook count
- Thread safety: Use
Arc<RwLock<>>for shared state - Error handling: Always handle errors gracefully
- Configuration: Validate TOML syntax and required fields
- Integration: Enable required feature flags