11 - Debugging Guide
Tools, techniques, and checklists for diagnosing issues in the .NET backend.
Swagger Explorer
The fastest way to test any endpoint without the Flutter app.
- URL:
http://localhost:5000/swagger - Development only
- Lets you POST/GET any endpoint with request body editing + JWT auth
- Shows the exact request/response schemas from the controller code
Hangfire Dashboard
Watch background jobs in real-time.
- URL:
http://localhost:5000/hangfire - Shows: Enqueued, Processing, Succeeded, Failed, Scheduled, Retries
- Click any failed job to see the full exception stack trace
- Trigger manual retries with the "Requeue" button
Serilog Console Output
All logs go to the console in dev mode via Serilog. Structured logs with CorrelationId enrichment for tracing across requests.
LogTypical log output
[16:22:14 INF] Lead 8f2a1c9e: Starting OTP verification [CorrelationId: a3f4...]
[16:22:14 INF] Lead 8f2a1c9e: OTP verified successfully. State=OTP_VERIFIED [CorrelationId: a3f4...]
[16:22:14 INF] Lead 8f2a1c9e: Bank auto-verify triggered via Decentro (first time) [CorrelationId: a3f4...]
[16:22:14 INF] Lead 8f2a1c9e: Bank auto-verify MOCKED [CorrelationId: a3f4...]
[16:22:14 INF] HTTP POST /api/v1/registration/otp/verify responded 200 in 83ms [CorrelationId: a3f4...]
EF Core Query Logs
Enable SQL query logging by setting Microsoft.EntityFrameworkCore.Database.Command: Information in appsettings.Development.json. Every generated SQL statement will appear in the console.
appsettings.Development.jsonJSON
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft.AspNetCore": "Information",
"Microsoft.EntityFrameworkCore.Database.Command": "Information"
}
}
}
API Audit Log Table
Every external provider call is logged to ekyc.api_audit_log. Query this when you want to see what the backend sent to an external provider and what came back.
SQLInspect audit log
SELECT provider, api_name, method, response_status_code, duration_ms, is_success, is_mock, error_message, created_at
FROM ekyc.api_audit_log
WHERE lead_id = '8f2a1c9e-...'
ORDER BY created_at DESC;
Journey Activity Log Table
Human-readable per-lead timeline populated by ActivityLogWriterService. Shows every significant event in the user journey.
SQLInspect activity log
SELECT stage_number, stage_name, category, severity, message, provider, is_success, created_at
FROM ekyc.journey_activity_logs
WHERE lead_id = '8f2a1c9e-...'
ORDER BY created_at;
Breakpoints (Visual Studio / Rider)
- Open the solution
backend/MO.Ekyc.sln - Set startup project to
MO.Ekyc.Api - Set breakpoints in controller actions or service methods
- F5 (Run with debugger)
- Make a request from Swagger or the Flutter app
- Step through with F10 / F11
Set one in ProviderChainExecutor.ExecuteAsync to watch every external call flow. Set one in any RequiresStage-decorated controller to observe the filter running.
Common Issues Checklists
"500 Internal Server Error"
- Check console for the Serilog stack trace
- Check
api_audit_logif it was an external call failing - Check Hangfire dashboard if a background job threw
- Check database connectivity - did PostgreSQL restart?
"409 Conflict" from StageValidationFilter
- The lead's
current_stageis less than the required minimum for the endpoint - Check
SELECT lead_id, current_stage, current_state FROM ekyc.leads WHERE lead_id = '...'; - Either the client is calling endpoints out of order, or the lead state is stale
"401 Unauthorized"
- JWT token expired (default 15 min)
- Token signing key mismatch between issuer and validator
- Check
Jwt.SecretKeyin appsettings matches what issued the token
"403 Forbidden" on admin endpoints
- JWT does not contain the required role claim
- Check
AdminAuthorizationFilterfor the allowed roles - Look at the JWT payload on jwt.io
"Schema 'ekyc' does not exist"
- Connect to
ekyc3database and runCREATE SCHEMA ekyc; - Then call the Dev endpoint
/api/v1/dev/purge-allor runDatabase.EnsureCreatedAsync()from a dev controller to create tables
External provider call returns real data in mock mode
- Check
MockMode:GlobalMockEnabledistrueinappsettings.Development.json - Check
provider_configurations.mock_modecolumn for that provider - Check the service constructor reads the config value correctly
- Look for "MOCKED" log messages - their absence means mock mode is off
Hangfire jobs stuck in "Enqueued"
- Check that
AddHangfireServer()was called in Program.cs (otherwise no worker is processing) - Check Hangfire PostgreSQL tables exist (
hangfire.job,hangfire.state, etc.) - Restart the API process
Postman / curl Testing
BashFull journey via curl
# Step 1: Arrival
curl "http://localhost:5000/api/v1/journey/init?device_type=DESKTOP"
# Step 2: Signup
curl -X POST http://localhost:5000/api/v1/registration/signup \
-H "Content-Type: application/json" \
-d '{"mobileNumber":"9876543210","fullName":"Test User","consentTerms":true,"consentBankAutoverify":true}'
# Step 3: Verify OTP (fixed 1234 in mock mode)
curl -X POST http://localhost:5000/api/v1/registration/otp/verify \
-H "Content-Type: application/json" \
-H "X-Lead-Id: <leadId-from-step-2>" \
-d '{"leadId":"...","otp":"1234"}'