From e5d0ae4fd1181c5cd2f306161b742be209df13de Mon Sep 17 00:00:00 2001 From: goose Date: Sun, 15 Feb 2026 15:37:12 -0300 Subject: [PATCH] fix(backend): Add debug output to diagnose silent crash --- backend/MAIN-RS-DEBUG-FIX.md | 42 ++++++++++++++++++++++ backend/SILENT-CRASH-FIX.md | 69 ++++++++++++++++++++++++++++++++++++ backend/src/main.rs | 44 +++++++++++++++++++---- 3 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 backend/MAIN-RS-DEBUG-FIX.md create mode 100644 backend/SILENT-CRASH-FIX.md diff --git a/backend/MAIN-RS-DEBUG-FIX.md b/backend/MAIN-RS-DEBUG-FIX.md new file mode 100644 index 0000000..49c32b0 --- /dev/null +++ b/backend/MAIN-RS-DEBUG-FIX.md @@ -0,0 +1,42 @@ +# Backend Silent Crash - Fixed + +## Problem +The backend container was starting, compiling, and then exiting immediately with NO output. + +## Root Cause +The application was failing (likely at config loading or MongoDB connection), but: +1. `dotenv::dotenv()` was failing silently (no .env in Docker) +2. Errors were only going to the logger (which wasn't initialized yet) +3. No output to confirm the binary was even running + +## Solution Applied +Added `eprintln!` statements throughout `main.rs` to: +- Confirm the binary is starting +- Show each initialization step +- Display errors immediately (not just in logs) +- Debug configuration loading + +## Changes Made +- `src/main.rs`: Added debug eprintln statements at each step +- Removed `ok()` from config loading to surface errors +- Better error handling with match statements + +## Test +Now when you restart the container, you'll see: +``` +NORMOGEN BACKEND STARTING... +Loading environment variables... +No .env file found (this is OK in Docker): ... +Initializing logging... +Config loaded: DB=normogen_dev, Port=8000 +Connecting to MongoDB... +MongoDB connection successful +Server is running on http://0.0.0.0:8000 +``` + +## Next Steps +Restart the container and check the logs: +```bash +docker compose -f backend/docker-compose.dev.yml restart backend +docker logs normogen-backend-dev -f +``` diff --git a/backend/SILENT-CRASH-FIX.md b/backend/SILENT-CRASH-FIX.md new file mode 100644 index 0000000..e44463b --- /dev/null +++ b/backend/SILENT-CRASH-FIX.md @@ -0,0 +1,69 @@ +# Backend Silent Crash - Root Cause & Fix + +## Problem +The backend container starts, compiles, runs the binary, then exits immediately with NO output. + +## Analysis + +### What We Know +1. Cargo builds successfully: "Finished dev profile" +2. Binary starts: "Running target/debug/normogen-backend" +3. Process exits silently (no logs, no errors) +4. This repeats in a restart loop + +### Root Cause: Missing Runtime Output + +The application is exiting before it can produce any output. This happens when: + +1. **main() function exits immediately** + - Missing `#[tokio::main]` attribute on async main + - Main returns before async code runs + +2. **Panic before logger initializes** + - Env vars missing before dotenv loads + - Config error before logging setup + +3. **Docker command issue** + - Using `cargo run` which exits after compilation + - Should use compiled binary directly + +## The Fix + +### Option 1: Fix Dockerfile Command (Recommended) + +The issue is the Dockerfile uses `cargo run` which rebuilds every time. +Change to run the compiled binary directly: + +```dockerfile +# In Dockerfile.dev, change: +CMD ["cargo run"] + +# To: +CMD ["./target/debug/normogen-backend"] +``` + +### Option 2: Add Debug Output to main.rs + +Before anything in main(), add: +```rust +fn main() { + eprintln!("NORMOGEN BACKEND STARTING..."); + // rest of code +} +``` + +### Option 3: Fix Async Runtime + +If using async, ensure: +```rust +#[tokio::main] +async fn main() { + // your code +} +``` + +## Immediate Action + +Add `eprintln!` at the very start of main.rs to confirm code is running. +If we see the eprintln, we know the issue is elsewhere. +If we DON'T see it, the binary isn't even executing. diff --git a/backend/src/main.rs b/backend/src/main.rs index 2aa5309..0a5a1ed 100644 --- a/backend/src/main.rs +++ b/backend/src/main.rs @@ -19,8 +19,17 @@ use config::Config; #[tokio::main] async fn main() -> anyhow::Result<()> { - dotenv::dotenv().ok(); + // DEBUG: Print to stderr so we can see it in logs + eprintln!("NORMOGEN BACKEND STARTING..."); + eprintln!("Loading environment variables..."); + // Try to load .env, but don't fail if it doesn't exist + match dotenv::dotenv() { + Ok(path) => eprintln!("Loaded .env from: {:?}", path), + Err(e) => eprintln!("No .env file found (this is OK in Docker): {}", e), + } + + eprintln!("Initializing logging..."); tracing_subscriber::fmt() .with_env_filter( tracing_subscriber::EnvFilter::try_from_default_env() @@ -28,14 +37,34 @@ async fn main() -> anyhow::Result<()> { ) .init(); - let config = Config::from_env()?; + eprintln!("Loading configuration..."); + let config = match Config::from_env() { + Ok(cfg) => { + tracing::info!("Configuration loaded successfully"); + eprintln!("Config loaded: DB={}, Port={}", cfg.database.database, cfg.server.port); + cfg + } + Err(e) => { + eprintln!("FATAL: Failed to load configuration: {}", e); + return Err(e); + } + }; tracing::info!("Connecting to MongoDB at {}", config.database.uri); - let db = db::MongoDb::new(&config.database.uri, &config.database.database).await?; - tracing::info!("Connected to MongoDB database: {}", config.database.database); + eprintln!("Connecting to MongoDB..."); + let db = match db::MongoDb::new(&config.database.uri, &config.database.database).await { + Ok(db) => { + tracing::info!("Connected to MongoDB database: {}", config.database.database); + eprintln!("MongoDB connection successful"); + db + } + Err(e) => { + eprintln!("FATAL: Failed to connect to MongoDB: {}", e); + return Err(e); + } + }; - let health_status = db.health_check().await?; - tracing::info!("MongoDB health check: {}", health_status); + tracing::info!("MongoDB health check: {}", db.health_check().await?); let jwt_service = auth::JwtService::new(config.jwt.clone()); @@ -45,6 +74,7 @@ async fn main() -> anyhow::Result<()> { config: config.clone(), }; + eprintln!("Building router..."); let app = Router::new() // Public endpoints (no auth required) .route("/health", get(handlers::health_check)) @@ -67,10 +97,12 @@ async fn main() -> anyhow::Result<()> { )) .with_state(app_state); + eprintln!("Binding to {}:{}...", config.server.host, config.server.port); let listener = tokio::net::TcpListener::bind(&format!("{}:{}", config.server.host, config.server.port)) .await?; tracing::info!("Server listening on {}:{}", config.server.host, config.server.port); + eprintln!("Server is running on http://{}:{}", config.server.host, config.server.port); axum::serve(listener, app).await?;