BaseService Issues
BaseService Issues
Section titled “BaseService Issues”BaseService provides one-line service startup with automatic infrastructure setup. This guide helps diagnose and resolve common BaseService initialization and lifecycle issues.
Quick Fix
Section titled “Quick Fix”# Check service startup logsdocker logs my-service 2>&1 | grep -E "BaseService|started|error|failed"
# Verify configurationdocker exec my-service env | grep -E "SERVICE_|RABBITMQ|DATABASE|REDIS"
# Test service healthcurl http://localhost:3000/health
# Check handler discoverydocker logs my-service 2>&1 | grep "Handler discovery"Common Problems
Section titled “Common Problems”1. Service Startup Failed
Section titled “1. Service Startup Failed”Symptoms:
- Service crashes during
BaseService.start() - Container exits immediately after starting
- Error: “ServiceStartupError”
- Logs show initialization failures
Diagnostic Steps:
# Check startup sequencedocker logs my-service 2>&1 | tail -100
# Look for specific initialization phase failuresdocker logs my-service 2>&1 | grep -E "Connecting|Initializing|Discovering|Broadcasting"
# Check exit codedocker inspect my-service | jq '.[0].State.ExitCode'Common Causes:
A. Configuration Validation Failed
// Error from ServiceConfig.tsthrow new Error('Service name is required and cannot be empty'); // Line 262throw new Error('Service name must be lowercase alphanumeric with hyphens (DNS compliant)'); // Line 267Solution:
// ✓ CORRECT service configurationawait BaseService.start({ name: 'user-service', // Required: DNS-compliant (lowercase, hyphens) version: '1.0.0', // Required: Semantic version description: 'User management', // Optional but recommended handlersPath: './src' // Optional: Defaults to './src'});
// ❌ WRONG configurationsawait BaseService.start({ name: 'UserService', // Error: Must be lowercase version: '1.0.0'});
await BaseService.start({ name: 'user_service', // Error: Use hyphens, not underscores version: '1.0.0'});
await BaseService.start({ name: '', // Error: Cannot be empty version: '1.0.0'});B. Database Configuration Invalid
// Errors from ServiceConfig.ts database validationthrow new Error('Database host is required'); // Line 647throw new Error('Database name is required'); // Line 651throw new Error('Database username is required'); // Line 655throw new Error('Database password is required'); // Line 659throw new Error('Database port must be between 1 and 65535'); // Line 663throw new Error('Database max connections must be at least 1'); // Line 667Solution:
# docker-compose.yml - Complete database configurationservices: my-service: environment: # Full database URL (recommended) - DATABASE_URL=postgresql://postgres:postgres@postgres:5432/platform
# OR individual components - DATABASE_HOST=postgres - DATABASE_PORT=5432 - DATABASE_NAME=platform - DATABASE_USER=postgres - DATABASE_PASSWORD=postgres - DATABASE_MAX_CONNECTIONS=20 - DATABASE_SSL=falseC. Missing Required Environment Variables
// Check ServiceConfig for required variablesthrow new Error( 'Either RABBITMQ_URL or RABBITMQ_HOST must be provided'); // ServiceConfig.ts:348Solution:
Provide all required configuration:
services: my-service: environment: # Service identity (REQUIRED) - SERVICE_NAME=my-service - SERVICE_VERSION=1.0.0
# Message bus (REQUIRED) - RABBITMQ_URL=amqp://admin:admin123@rabbitmq:5672
# Database (REQUIRED for event sourcing services) - DATABASE_URL=postgresql://postgres:postgres@postgres:5432/platform
# Optional but recommended - LOG_LEVEL=info - NODE_ENV=development - REDIS_URL=redis://redis:63792. Database Connection Failed
Section titled “2. Database Connection Failed”Symptoms:
- Error: “DatabaseConnectionError”
- Service starts but cannot access database
- “Pool not initialized” errors
- Database query failures
Diagnostic Steps:
# Check database connectivitydocker exec my-service nc -zv postgres 5432
# Test database from service containerdocker exec my-service psql -h postgres -U postgres -d platform -c "SELECT 1;"
# Check connection pool statusdocker logs my-service 2>&1 | grep -E "Database|Pool|connection"
# Verify DATABASE_URLdocker exec my-service env | grep DATABASECommon Errors:
// From DatabaseManager.tsthrow new Error('Pool not initialized'); // Line 151throw new Error('Database pool not available'); // Lines 183, 193, 225, 230
// From HealthMonitor.tsthrow new DatabaseConnectionError( 'Failed to connect to database', correlationId, { host, port, database }); // Line 715Solution:
A. Ensure PostgreSQL is Running
# Check PostgreSQL containerdocker ps | grep postgres
# If not running, start itdocker compose up -d postgres
# Wait for readydocker logs postgres 2>&1 | grep "database system is ready to accept connections"B. Fix Connection String
# ✓ CORRECT connection stringsDATABASE_URL=postgresql://postgres:postgres@postgres:5432/platformDATABASE_URL=postgresql://user:pass@postgres:5432/mydbDATABASE_URL=postgresql://user:pass@postgres:5432/mydb?sslmode=require
# ❌ WRONG connection stringsDATABASE_URL=postgresql://localhost:5432/platform # Use service name, not localhostDATABASE_URL=postgres://postgres@postgres/platform # Missing password and portC. Initialize Database Schema
// BaseService automatically initializes event store schema// But you may need to create database first
// Manual database creation if neededconst client = new Client({ host: 'postgres', port: 5432, user: 'postgres', password: 'postgres', database: 'postgres' // Connect to default postgres db});
await client.connect();await client.query('CREATE DATABASE platform;');await client.end();D. Configure Connection Pool
// Adjust pool size for high-traffic servicesawait BaseService.start({ name: 'user-service', version: '1.0.0', database: { url: process.env.DATABASE_URL, poolSize: 50, // Increase from default 20 connectionTimeoutMillis: 10000, idleTimeoutMillis: 30000, ssl: false }});3. Message Bus Connection Failed
Section titled “3. Message Bus Connection Failed”Symptoms:
- Error: “MessageBusConnectionError”
- Cannot publish or receive messages
- Service starts but RabbitMQ not connected
- “Connection closed” errors
Diagnostic Steps:
# Check RabbitMQ connectivitydocker exec my-service nc -zv rabbitmq 5672
# Check RabbitMQ statusdocker ps | grep rabbitmqdocker logs rabbitmq | tail -50
# Test RabbitMQ management APIcurl http://localhost:15672/api/overview
# Check service connection logsdocker logs my-service 2>&1 | grep -E "RabbitMQ|MessageBus|connection"Common Errors:
// From HealthMonitor.tsthrow new MessageBusConnectionError( 'Failed to connect to message bus', correlationId, { urls: config.urls }); // Line 760Solution:
A. Verify RabbitMQ is Running and Healthy
# Start RabbitMQdocker compose up -d rabbitmq
# Wait for RabbitMQ to be readydocker logs rabbitmq 2>&1 | grep "Server startup complete"
# Check healthdocker exec rabbitmq rabbitmq-diagnostics pingB. Fix Connection URL
services: my-service: environment: # ✓ CORRECT formats - RABBITMQ_URL=amqp://admin:admin123@rabbitmq:5672 - RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672/vhost
# ❌ WRONG formats # - RABBITMQ_URL=amqp://localhost:5672 # Use service name # - RABBITMQ_URL=rabbitmq:5672 # Missing protocolC. Add Service Dependencies
services: my-service: depends_on: rabbitmq: condition: service_healthy postgres: condition: service_healthy
rabbitmq: healthcheck: test: rabbitmq-diagnostics -q ping interval: 10s timeout: 5s retries: 5
postgres: healthcheck: test: ["CMD-SHELL", "pg_isready -U postgres"] interval: 10s timeout: 5s retries: 5D. Configure Connection Retry
// BaseService automatically retries connections// But you can configure retry behaviorawait BaseService.start({ name: 'user-service', version: '1.0.0', messageBus: { urls: [process.env.RABBITMQ_URL!], connectionRetry: { maxAttempts: 10, initialDelayMs: 2000, maxDelayMs: 30000, backoffMultiplier: 2 } }});4. Handler Discovery Failed
Section titled “4. Handler Discovery Failed”Symptoms:
- Error: “HandlerDiscoveryError”
- No handlers discovered
- Handlers discovered but not registered
- Contract broadcasting failures
Diagnostic Steps:
# Check handler discovery logsdocker logs my-service 2>&1 | grep "Handler discovery"
# Should show:# Handler discovery completed {# commandHandlers: 5,# queryHandlers: 3,# eventHandlers: 2# }
# Verify handler files existdocker exec my-service ls -R /app/dist/commands /app/dist/queries /app/dist/events
# Check handler exportsdocker exec my-service grep -r "export class.*Handler" /app/dist/Common Errors:
// From BaseServiceErrors.tsexport class HandlerDiscoveryError extends BaseServiceError { code = 'HANDLER_DISCOVERY_ERROR';}
export class HandlerRegistrationError extends BaseServiceError { code = 'HANDLER_REGISTRATION_ERROR';}Solution:
A. Ensure Handlers in Correct Directories
src/├── commands/ # Command handlers here│ ├── CreateUserHandler.ts│ └── UpdateUserHandler.ts├── queries/ # Query handlers here│ ├── GetUserHandler.ts│ └── ListUsersHandler.ts└── events/ # Event handlers here ├── UserCreatedHandler.ts └── UserUpdatedHandler.tsB. Add Required Decorators
// ✓ CORRECT: Handler with decoratorimport { CommandHandler } from '@banyanai/platform-cqrs';import { CreateUserCommand } from '../contracts/commands.js';
@CommandHandler(CreateUserCommand)export class CreateUserHandler { async handle(command: CreateUserCommand) { // Implementation }}
// ❌ WRONG: Missing decoratorexport class CreateUserHandler { async handle(command: CreateUserCommand) { // Won't be discovered! }}C. Verify Handler Exports
// ❌ WRONG: Not exportedclass CreateUserHandler { }
// ❌ WRONG: Default exportexport default class CreateUserHandler { }
// ✓ CORRECT: Named exportexport class CreateUserHandler { }D. Configure Custom Handlers Path
// If handlers are in non-standard locationawait BaseService.start({ name: 'user-service', version: '1.0.0', handlersPath: './dist/handlers' // Custom path});E. Check TypeScript Compilation
# Ensure TypeScript compiled successfullypnpm run build
# Check for .js files in distls -R dist/commands dist/queries dist/events
# Verify decorators preservedgrep -r "@CommandHandler" dist/# Should show decorator calls5. Health Check Failed
Section titled “5. Health Check Failed”Symptoms:
- Error: “HealthCheckError”
- Health endpoint returns unhealthy status
- Service marked degraded or unavailable
- Dependency health checks failing
Diagnostic Steps:
# Check health endpointcurl http://localhost:3000/health | jq
# View health check logsdocker logs my-service 2>&1 | grep -E "Health|check"
# Check dependency healthcurl http://localhost:3000/health/dependencies | jqCommon Error:
// From HealthMonitor.tsthrow new HealthCheckError(`Health check '${name}' not found`); // Line 237Solution:
A. Verify All Dependencies Healthy
# Check each dependencydocker ps --format "table {{.Names}}\t{{.Status}}"
# Test databasedocker exec my-service psql -h postgres -U postgres -d platform -c "SELECT 1;"
# Test RabbitMQdocker exec my-service nc -zv rabbitmq 5672
# Test Redis (if used)docker exec my-service redis-cli -h redis pingB. Add Custom Health Checks
import { BaseService } from '@banyanai/platform-base-service';
await BaseService.start({ name: 'user-service', version: '1.0.0'});
// Add custom health check after startupconst healthMonitor = BaseService.getHealthMonitor();
healthMonitor.registerHealthCheck('external-api', async () => { try { const response = await fetch('https://api.example.com/health'); return response.ok; } catch { return false; }});C. Configure Health Check Thresholds
await BaseService.start({ name: 'user-service', version: '1.0.0', health: { checkInterval: 30000, // Check every 30 seconds unhealthyThreshold: 3, // Mark unhealthy after 3 failures degradedThreshold: 2, // Mark degraded after 2 failures timeout: 5000 // 5 second timeout per check }});6. Service Shutdown Errors
Section titled “6. Service Shutdown Errors”Symptoms:
- Error: “ServiceShutdownError”
- Service doesn’t stop gracefully
- Connections not closed properly
- Database connections leak
Diagnostic Steps:
# Check shutdown logsdocker logs my-service 2>&1 | grep -E "shutdown|stopping|closed"
# Look for connection cleanupdocker logs my-service 2>&1 | grep -E "Closing|Disconnecting"
# Check for hanging connectionsdocker exec postgres psql -U postgres -d platform -c \ "SELECT pid, usename, application_name, state, query FROM pg_stat_activity WHERE application_name LIKE 'my-service%';"Solution:
A. Handle SIGTERM/SIGINT Properly
// BaseService automatically handles shutdown signals// But you can add custom cleanup
process.on('SIGTERM', async () => { console.log('SIGTERM received, shutting down gracefully');
// Custom cleanup before BaseService shutdown await myCustomCleanup();
// BaseService.shutdown() called automatically process.exit(0);});B. Implement Cleanup Hooks
import { BaseService } from '@banyanai/platform-base-service';
await BaseService.start({ name: 'user-service', version: '1.0.0', onShutdown: async () => { // Called before BaseService cleanup console.log('Running custom shutdown logic');
// Close custom connections await myDatabasePool.end(); await myCache.disconnect(); }});C. Set Graceful Shutdown Timeout
services: my-service: stop_grace_period: 30s # Allow 30 seconds for graceful shutdown
environment: - SHUTDOWN_TIMEOUT=25000 # 25 seconds (less than stop_grace_period)7. Service Client Injection Failed
Section titled “7. Service Client Injection Failed”Symptoms:
- Error: “ServiceClientInjectionError”
- Cannot inject service clients
- Client not found for service
- Circular dependency errors
Diagnostic Steps:
# Check client injection logsdocker logs my-service 2>&1 | grep -E "client|injection|dependency"
# Verify service discovery has target servicecurl http://localhost:3001/api/services | jq '.services[] | select(.name=="target-service")'
# Check handler constructorgrep -A5 "constructor" src/commands/CreateUserHandler.tsCommon Error:
// From BaseServiceErrors.tsexport class ServiceClientInjectionError extends BaseServiceError { code = 'SERVICE_CLIENT_INJECTION_ERROR';}Solution:
A. Use Proper Client Injection
import { CommandHandler } from '@banyanai/platform-cqrs';import { InjectClient } from '@banyanai/platform-base-service';import type { UserServiceClient } from '@banyanai/user-service-client';
@CommandHandler(CreateOrderCommand)export class CreateOrderHandler { constructor( @InjectClient('user-service') private userService: UserServiceClient ) {}
async handle(command: CreateOrderCommand) { // Use injected client const user = await this.userService.queries.getUser({ userId: command.userId });
// Create order... }}B. Ensure Target Service Registered
# Check if target service is in service discoverycurl http://localhost:3001/api/services | jq '.services[].name'
# Should include target service name# If not, start the target servicedocker compose up -d user-serviceC. Avoid Circular Dependencies
// ❌ WRONG: Circular dependency// Service A depends on Service B// Service B depends on Service A
// ✓ CORRECT: One-way dependency// Service A depends on Service B// Service B depends on shared library or event busAdvanced Diagnostics
Section titled “Advanced Diagnostics”Enable Debug Logging
Section titled “Enable Debug Logging”services: my-service: environment: - LOG_LEVEL=debug - DEBUG=base-service:*View detailed logs:
docker logs my-service 2>&1 | grep -E "DEBUG|BaseService"Check Initialization Sequence
Section titled “Check Initialization Sequence”# BaseService initialization order:# 1. Configuration validation# 2. Database connection# 3. Message bus connection# 4. Handler discovery# 5. Contract broadcasting# 6. Service registration# 7. Health checks# 8. Ready state
docker logs my-service 2>&1 | grep -E "Step|Phase|Initializing"Monitor Resource Usage
Section titled “Monitor Resource Usage”# Check memory usagedocker stats my-service --no-stream
# Check CPU usagedocker stats my-service --no-stream --format "table {{.Name}}\t{{.CPUPerc}}"
# Check connection countdocker exec postgres psql -U postgres -d platform -c \ "SELECT count(*) FROM pg_stat_activity WHERE application_name LIKE 'my-service%';"Test Service Manually
Section titled “Test Service Manually”import { BaseService } from '@banyanai/platform-base-service';
async function test() { try { await BaseService.start({ name: 'test-service', version: '1.0.0' });
console.log('✓ Service started successfully');
// Test operations const healthMonitor = BaseService.getHealthMonitor(); const health = await healthMonitor.getHealth(); console.log('Health:', health);
} catch (error) { console.error('✗ Service startup failed:', error); process.exit(1); }}
test();Best Practices
Section titled “Best Practices”1. Always Validate Configuration
Section titled “1. Always Validate Configuration”// Validate before starting serviceconst config = { name: process.env.SERVICE_NAME, version: process.env.SERVICE_VERSION, database: { url: process.env.DATABASE_URL }, messageBus: { urls: [process.env.RABBITMQ_URL!] }};
// Check required fieldsif (!config.name || !config.version) { throw new Error('SERVICE_NAME and SERVICE_VERSION required');}
await BaseService.start(config);2. Use Health Checks
Section titled “2. Use Health Checks”// Register custom health checks for dependenciesconst healthMonitor = BaseService.getHealthMonitor();
healthMonitor.registerHealthCheck('cache', async () => { try { await redis.ping(); return true; } catch { return false; }});3. Implement Graceful Shutdown
Section titled “3. Implement Graceful Shutdown”let isShuttingDown = false;
process.on('SIGTERM', async () => { if (isShuttingDown) return; isShuttingDown = true;
console.log('Shutting down gracefully');
// Stop accepting new requests await stopAcceptingRequests();
// Wait for in-flight requests await waitForInFlightRequests();
// BaseService cleanup happens automatically});4. Monitor Startup Time
Section titled “4. Monitor Startup Time”const startTime = Date.now();
await BaseService.start({ /* config */ });
const duration = Date.now() - startTime;console.log(`Service started in ${duration}ms`);
// Alert if startup takes too longif (duration > 30000) { console.warn('Slow startup detected');}Related Documentation
Section titled “Related Documentation”Summary
Section titled “Summary”Most BaseService issues are caused by:
- Configuration errors - Validate service name, version, and required config
- Missing dependencies - Ensure PostgreSQL and RabbitMQ are running and healthy
- Handler discovery failures - Place handlers in correct folders with proper decorators
- Connection failures - Use correct URLs and service names (not localhost)
- Missing environment variables - Provide all required configuration
Use docker logs and health checks to diagnose issues. BaseService provides detailed logging of initialization steps.