Documentation Index
Fetch the complete documentation index at: https://mintlify.com/bidewio/better-openclaw/llms.txt
Use this file to discover all available pages before exploring further.
The validator performs comprehensive validation of resolved service configurations and generated Docker Compose YAML to catch errors before deployment.
Overview
The validator runs multiple checks on a resolved stack configuration:
- Port conflict detection
- Volume name uniqueness
- Environment variable completeness
- Network consistency
- Dependency graph validation (cycle detection)
- YAML syntax validation
- Domain format validation
validate
Validates a complete generated stack configuration.
import { validate } from '@better-openclaw/core';
import type { ValidationResult } from '@better-openclaw/core';
const result: ValidationResult = validate(
resolved,
composedYaml,
{
domain: 'example.com',
generateSecrets: true
}
);
if (!result.valid) {
console.error('Validation errors:');
result.errors.forEach(err => {
console.error(` [${err.type}] ${err.message}`);
});
}
if (result.warnings.length > 0) {
console.warn('Validation warnings:');
result.warnings.forEach(warn => {
console.warn(` [${warn.type}] ${warn.message}`);
});
}
Parameters
Resolved service configuration from the resolver
Generated Docker Compose YAML content to validate
Optional validation configuration
Domain name to validate format
Whether secrets are auto-generated (affects required env var checking)
Returns
True if no errors were found (warnings don’t affect validity)
Blocking validation errors that must be fixed
Non-blocking warnings that should be reviewed
Validation Checks
Port Conflict Detection
Ensures no two services expose the same host port.
// Error example:
{
type: 'port_conflict',
message: 'Port 8080 is used by both "grafana" and "n8n"'
}
Fix: Use portOverrides in generation input:
const input = {
projectName: 'stack',
services: ['grafana', 'n8n'],
portOverrides: {
grafana: { 3000: 3150 } // Change host port
}
};
Volume Uniqueness
Verifies volume names are not shared across different services (bind mounts excluded).
// Error example:
{
type: 'volume_conflict',
message: 'Volume name "data" is used by both "redis" and "postgresql"'
}
Environment Variable Completeness
Checks that required environment variables have defaults or will be generated.
// Error example:
{
type: 'missing_env',
message: 'Required environment variable "DATABASE_URL" for "n8n" has no default value'
}
// Warning example:
{
type: 'secret_needed',
message: 'Secret "POSTGRES_PASSWORD" for "PostgreSQL" needs to be configured manually'
}
Environment Variable Validation
Validates default values against regex patterns if specified.
// Warning example:
{
type: 'env_validation',
message: 'Environment variable "EMAIL" for "n8n" default value does not match validation pattern: ^[^@]+@[^@]+\.[^@]+$'
}
Network Consistency
Warns if services are not on the openclaw-network.
// Warning example:
{
type: 'network',
message: 'Service "custom-service" is not on openclaw-network — it may not be reachable from OpenClaw'
}
Dependency Graph Validation
Detects circular dependencies using depth-first search.
// Error example:
{
type: 'cycle',
message: 'Circular dependency detected involving "service-a"'
}
YAML Syntax Validation
Parses the generated YAML to ensure it’s syntactically valid.
// Error example:
{
type: 'yaml_invalid',
message: 'Generated YAML is not valid: Unexpected token at line 42'
}
Domain Format Validation
Validates domain name format when provided.
// Error example:
{
type: 'invalid_domain',
message: '"not_a_domain" is not a valid domain name'
}
Valid formats:
example.com
subdomain.example.com
my-site.co.uk
Invalid formats:
example (no TLD)
http://example.com (includes protocol)
example.com/path (includes path)
-example.com (starts with hyphen)
Types
ValidationResult
interface ValidationResult {
valid: boolean;
errors: ResolverError[];
warnings: Warning[];
}
ResolverError
interface ResolverError {
type: 'port_conflict' | 'volume_conflict' | 'missing_env' | 'cycle' | 'yaml_invalid' | 'invalid_domain';
message: string;
}
Warning
interface Warning {
type: 'secret_needed' | 'env_validation' | 'network';
message: string;
}
Examples
Basic Validation
import { resolve, compose, validate } from '@better-openclaw/core';
const resolved = resolve({
services: ['redis', 'postgresql'],
platform: 'linux/amd64'
});
const yaml = compose(resolved, {
projectName: 'my-stack',
platform: 'linux/amd64'
});
const validation = validate(resolved, yaml);
if (!validation.valid) {
throw new Error('Validation failed');
}
Handling Validation Errors
const validation = validate(resolved, yaml, {
domain: 'example.com',
generateSecrets: true
});
// Group errors by type
const errorsByType = validation.errors.reduce((acc, err) => {
acc[err.type] = acc[err.type] || [];
acc[err.type].push(err);
return acc;
}, {} as Record<string, ResolverError[]>);
if (errorsByType.port_conflict) {
console.error('Port conflicts detected:');
errorsByType.port_conflict.forEach(err => {
console.error(` - ${err.message}`);
});
}
Warning Collection
const validation = validate(resolved, yaml, { generateSecrets: false });
// Collect secrets that need manual configuration
const secretWarnings = validation.warnings.filter(w => w.type === 'secret_needed');
if (secretWarnings.length > 0) {
console.warn('⚠️ The following secrets need manual configuration:');
secretWarnings.forEach(w => {
console.warn(` - ${w.message}`);
});
}
Integration with Generation Pipeline
import { generate } from '@better-openclaw/core';
import { ValidationError } from '@better-openclaw/core';
try {
const result = generate({
projectName: 'my-stack',
services: ['redis', 'postgresql', 'n8n'],
proxy: 'caddy',
domain: 'example.com',
platform: 'linux/amd64',
generateSecrets: true
});
// generate() calls validate() internally and throws ValidationError if invalid
console.log('✅ Stack generated successfully');
} catch (error) {
if (error instanceof ValidationError) {
console.error('Validation failed:', error.message);
}
}
Validation in CLI
The @better-openclaw/cli uses the validator to prevent generating invalid configurations:
# CLI validates before writing files
better-openclaw generate \
--services redis,postgresql,n8n \
--proxy caddy \
--domain example.com
# Output on error:
# ❌ Validation failed:
# [port_conflict] Port 8080 is used by both "grafana" and "n8n"
# [invalid_domain] "not_a_domain" is not a valid domain name
Best Practices
-
Always validate before deployment
const validation = validate(resolved, yaml);
if (!validation.valid) {
throw new Error('Cannot deploy invalid configuration');
}
-
Don’t ignore warnings
if (validation.warnings.length > 0) {
console.warn('⚠️ Warnings detected — review before deploying:');
validation.warnings.forEach(w => console.warn(` - ${w.message}`));
}
-
Validate domain early
// Validate domain before generating entire stack
const validation = validate(resolved, '', { domain: userInput.domain });
if (validation.errors.some(e => e.type === 'invalid_domain')) {
throw new Error('Invalid domain format');
}
-
Use typed errors
import { ValidationError } from '@better-openclaw/core';
try {
const result = generate(input);
} catch (error) {
if (error instanceof ValidationError) {
// Handle validation-specific errors
}
}
See Also