Skip to content

Troubleshooting Tips

Common issues and their solutions when building Ignis applications.

1. Application Fails to Start

SymptomCauseSolution
App exits immediatelyMissing environment variablesCheck .env file has all required vars (especially APP_ENV_APPLICATION_SECRET, APP_ENV_JWT_SECRET)
Connection error on startupDatabase unreachableVerify APP_ENV_POSTGRES_* values and PostgreSQL is running
Error: listen EADDRINUSEPort already in useChange APP_ENV_SERVER_PORT or stop conflicting process

Quick fix:

bash
# Check if PostgreSQL is running
psql -U postgres -c "SELECT 1;"

# Find process using port 3000
lsof -ti:3000 | xargs kill -9

2. API Endpoint Returns 404

CauseCheckFix
Controller not registeredMissing in application.tsAdd this.controller(MyController) to preConfigure()
Incorrect pathTypo in route pathVerify @controller({ path }) and route path match URL
Wrong base pathMissing /api prefixCheck path.base in application config

Debug routes:

typescript
// In application.ts config
export const appConfigs: IApplicationConfigs = {
  debug: {
    showRoutes: process.env.NODE_ENV !== 'production',
  },
};

This prints all registered routes on startup.

3. Dependency Injection Fails (Binding not found)

CauseExample ErrorFix
Resource not registeredBinding 'services.MyService' not foundAdd this.service(MyService) to preConfigure()
Wrong injection keyKey mismatch or typoUse BindingKeys.build() helper
Wrong namespaceUsing repository instead of serviceCheck correct namespace in @inject

Debug bindings:

typescript
// In postConfigure() method
async postConfigure(): Promise<void> {
  this.logger.info('Available bindings: %s',
    Array.from(this.bindings.keys())
  );
}

4. Authentication Fails (401 Unauthorized)

SymptomCauseSolution
401 Unauthorized on protected routeMissing Authorization headerAdd Authorization: Bearer <token> header
Token expiredJWT past expirationRequest new token from login endpoint
Invalid signatureWrong JWT_SECRETEnsure APP_ENV_JWT_SECRET matches across services
Malformed headerMissing "Bearer " prefixFormat: Bearer eyJhbGc...

Test with curl:

bash
curl -H "Authorization: Bearer YOUR_TOKEN" \
  http://localhost:3000/api/protected-route

5. General Debugging Tips

Enable detailed logging:

bash
# Enable debug mode via environment variable
DEBUG=true

Use method-scoped logging with .for():

typescript
class UserService {
  private logger = Logger.get('UserService');

  async createUser(data: CreateUserDto) {
    this.logger.for('createUser').info('Creating user: %j', data);
    // Output: [UserService-createUser] Creating user: {...}

    try {
      const user = await this.userRepo.create({ data });
      this.logger.for('createUser').info('User created: %s', user.id);
      return user;
    } catch (error) {
      this.logger.for('createUser').error('Failed: %s', error);
      throw error;
    }
  }
}

Common debugging commands:

bash
# View application logs
tail -f logs/app.log

# Check TypeScript compilation errors
bun run build

# Validate environment variables
cat .env | grep APP_ENV

Useful debugging patterns:

  • Use logger.for('methodName') to trace execution with method context
  • Use try-catch blocks to catch and log errors
  • Check database queries with Drizzle's logging: { logger: true }

Deep Dive: See Logger Helper for advanced logging configuration.

6. Request ID Tracking

Every request in Ignis is automatically assigned a unique requestId for log correlation. The RequestSpyMiddleware logs this ID at the start and end of each request.

Log output format:

[spy][abc123] START | Handling Request | forwardedIp: 192.168.1.1 | path: /api/users | method: GET
[spy][abc123] DONE  | Handling Request | forwardedIp: 192.168.1.1 | path: /api/users | method: GET | Took: 45.2 (ms)

Access request ID in handlers:

typescript
import { RequestSpyMiddleware } from '@venizia/ignis';

// Inside a controller method
async getUser(c: Context) {
  const requestId = c.get(RequestSpyMiddleware.REQUEST_ID_KEY);
  this.logger.info('[%s] Processing user request', requestId);
  // ...
}

Filtering logs by request:

bash
# Find all logs for a specific request
grep "abc123" logs/app.log

# Extract request timing
grep "\[spy\]\[abc123\]" logs/app.log

Why this matters:

  • Correlate logs across services in distributed systems
  • Debug specific user issues by their request ID
  • Measure request duration from START to DONE timestamps

7. Validation Error Debugging

When Zod validation fails, Ignis returns a structured error response. Understanding this format helps debug client-side issues.

Error response structure:

json
{
  "statusCode": 422,
  "message": "ValidationError",
  "requestId": "abc123",
  "details": {
    "cause": [
      {
        "path": "email",
        "message": "Invalid email",
        "code": "invalid_string",
        "expected": "email",
        "received": "string"
      }
    ]
  }
}

Common validation error codes:

CodeMeaningExample
invalid_typeWrong data typeExpected number, got string
invalid_stringString format invalidInvalid email or UUID format
too_smallValue below minimumString shorter than min length
too_bigValue above maximumNumber exceeds max value
invalid_enum_valueValue not in enumStatus must be 'ACTIVE' or 'INACTIVE'
unrecognized_keysExtra fields in requestStrict schema rejects unknown fields

Debugging tips:

  1. Check the path field - Shows which field failed validation
  2. Compare expected vs received - Identifies type mismatches
  3. Review schema definition - Ensure client sends correct format

Example: Debugging nested validation errors:

json
{
  "details": {
    "cause": [
      {
        "path": "address.zipCode",
        "message": "Expected string, received number",
        "code": "invalid_type",
        "expected": "string",
        "received": "number"
      }
    ]
  }
}

The path uses dot notation for nested objects. Here, address.zipCode means the zipCode field inside the address object is invalid.