Skip to main content

Troubleshooting Guide

Lofty Golden Oil ERP — Diagnosing and Resolving Common Issues


Table of Contents

  1. Login Issues
  2. Navigation Issues
  3. Inventory Issues
  4. Sales Issues
  5. Production Issues
  6. Accounting Issues
  7. Email Issues
  8. Performance Issues
  9. Server Issues
  10. Database Issues
  11. SSL Issues
  12. Common Error Messages

1. Login Issues

Wrong Password

Symptom: "Invalid username or password" error after entering credentials.

Solutions:

  1. Verify Caps Lock is off and password is typed correctly.
  2. Use the "Forgot Password" link on the login page to reset via email.
  3. As admin, reset the password directly:
    mysql -u lgo -p lgo_erp_prod
    UPDATE users SET password = '$2a$10$...' WHERE username = 'affected_user';
    Generate a new bcrypt hash in Node.js:
    const bcrypt = require('bcryptjs');
    console.log(await bcrypt.hash('new_password', 10));
  4. Verify the RESEND_API_KEY is valid if password reset emails are not arriving.

Account Locked / Inactive

Symptom: Login fails with no error message or "Account is inactive."

Solutions:

  1. Check user status in the database:
    SELECT id, username, status FROM users WHERE username = 'affected_user';
  2. If status is inactive, reactivate:
    UPDATE users SET status = 'active' WHERE username = 'affected_user';
  3. Contact an administrator to restore access.

Browser Cache Issues

Symptom: Login page loops, shows stale data, or behaves unexpectedly.

Solutions:

  1. Clear browser cookies for the site:
    • Chrome: Settings → Privacy → Clear browsing data → Cookies and site data
    • Firefox: Settings → Privacy → Cookies and site data → Clear
    • Safari: Preferences → Privacy → Manage Website Data → Remove
  2. Clear browser cache: Cmd+Shift+Delete (macOS) or Ctrl+Shift+Delete (Windows).
  3. Open an incognito/private window and try logging in.
  4. Try a different browser.

JWT Expired

Symptom: Suddenly logged out mid-session, or "Access denied" / "Token expired" errors.

Solutions:

  1. Log in again — JWT tokens expire after 24 hours.
  2. If this happens frequently, check that the server clock is correct:
    timedatectl status
  3. If using a proxy, ensure Authorization headers are forwarded correctly.
  4. Check that JWT_SECRET has not changed between server restarts (tokens are invalidated).

2. Navigation Issues

Missing Menu Items

Symptom: Expected menu items are not visible in the sidebar.

Causes and Solutions:

  1. Role-based visibility: Menu items are filtered by user role. Verify your role:
    SELECT username, role FROM users WHERE username = 'your_username';
  2. Available roles and their modules:
    RoleVisible Modules
    adminAll modules
    productionDashboard, Inventory, Production, Requisitions
    salesDashboard, Inventory, Sales, Customers, Requisitions
    accountantDashboard, Expenses, AR, Reports, Requisitions
  3. Contact an admin to change your role if needed.

Blank Page / White Screen

Symptom: After login, the main content area is blank or shows a white screen.

Solutions:

  1. Open browser Developer Tools (F12) → Console tab. Check for JavaScript errors.
  2. Common causes:
    • JavaScript bundle failed to load (network issue)
    • API returned unexpected data format
    • Component rendering error
  3. Check the browser Console for specific error messages.
  4. Verify the frontend build is current:
    ls -la /var/www/lgo-erp/client/dist/
  5. Rebuild if necessary:
    cd /home/opc/lofty-golden-oil-erp
    npm run build

Theme Issues

Symptom: Dark/light mode not applying correctly, colors look wrong, text unreadable.

Solutions:

  1. Toggle the theme using the theme switcher in the header.
  2. Check that index.css CSS variables are correctly defined.
  3. Clear browser cache — stale CSS may be served.
  4. Verify tailwind.config.js includes darkMode: 'class'.
  5. Check if the <html> element has the dark class:
    document.documentElement.classList.contains('dark')

3. Inventory Issues

Stock Not Updating

Symptom: Purchases or sales are recorded but stock quantity hasn't changed.

Solutions:

  1. Verify the purchase/sale was actually saved:
    SELECT * FROM purchases WHERE id = ?;
    SELECT * FROM sales WHERE id = ?;
  2. Check if stock updates are applied in the correct database transaction.
  3. Verify the product ID matches between the purchase/sale and inventory records.
  4. Check for pending inventory adjustments.
  5. Refresh the page — the data may not have re-fetched.

Wrong Stock Count

Symptom: Inventory quantity doesn't match physical count.

Solutions:

  1. Check for manual adjustments:
    SELECT * FROM inventory_adjustments WHERE product_id = ? ORDER BY created_at DESC;
  2. Verify all purchases have been received and recorded.
  3. Verify all sales have been recorded with correct quantities.
  4. Check for production runs that consumed raw materials.
  5. Create an inventory adjustment to correct the count:
    • Navigate to Inventory → Adjust Stock
    • Enter the correct quantity and reason
    • The adjustment is logged in the audit trail

Alerts Not Showing

Symptom: Low stock or expiry alerts not appearing on dashboard.

Solutions:

  1. Check the product's low_stock_threshold value:
    SELECT name, quantity, low_stock_threshold, expiry_date FROM inventory WHERE id = ?;
  2. Ensure the threshold is set (default is 100 litres).
  3. Check that expiry_date is set for expiry-related alerts.
  4. Verify the dashboard API returns alert data:
    curl http://localhost:4000/api/dashboard -H "Authorization: Bearer $TOKEN"

4. Sales Issues

Cannot Create Sale

Symptom: Sale form won't submit or shows validation errors.

Solutions:

  1. Customer must be active:
    SELECT id, name, status FROM customers WHERE id = ?;
    If status is inactive, reactivate or select a different customer.
  2. Product must be in stock:
    SELECT id, name, quantity, status FROM inventory WHERE id = ?;
    Quantity must be greater than 0 and status must be active.
  3. All required fields must be filled: Customer, product, quantity, and price.
  4. Quantity cannot exceed available stock.
  5. Check browser console for API error responses.

Invoice Not Generating

Symptom: Sale is recorded but no invoice is created.

Solutions:

  1. Check the sale status — invoice generation may be triggered by a specific status.
  2. Verify the AR invoices endpoint:
    curl http://localhost:4000/api/ar/invoices -H "Authorization: Bearer $TOKEN"
  3. Check the database for the invoice record:
    SELECT * FROM ar_invoices WHERE sale_id = ?;
  4. If missing, create the invoice manually via the AR module.

Receipt Not Printing

Symptom: Print button doesn't work or print preview is blank.

Solutions:

  1. Check printer connection and paper supply.
  2. Ensure the browser allows pop-ups for the site (print often opens a new window).
  3. Try Cmd+P / Ctrl+P to print the current page manually.
  4. Clear browser cache if the print template is stale.
  5. Check if a PDF download is available as an alternative.

5. Production Issues

Yield Percentage Wrong

Symptom: Calculated yield % doesn't match expected value.

Solutions:

  1. Verify input volume and output volume are correct:
    SELECT input_volume, output_volume, yield_percentage FROM production_runs WHERE id = ?;
  2. Yield % formula: (output_volume / input_volume) × 100
  3. Check for data entry errors (e.g., litres vs. kilograms).
  4. Verify all production runs for the batch are accounted for.

Cost Per Litre Wrong

Symptom: Production cost per litre doesn't match expectations.

Solutions:

  1. Cost per litre formula: total_input_cost / output_volume
  2. Verify input costs are correct:
    SELECT p.*, i.price FROM production_runs pr
    JOIN purchases p ON pr.purchase_id = p.id
    WHERE pr.id = ?;
  3. Check for shared costs (labour, overhead) that should be allocated.
  4. Verify the output volume reflects actual production (not planned).

6. Accounting Issues

Trial Balance Not Balancing

Symptom: Total debits ≠ total credits in the trial balance.

Solutions:

  1. This indicates unbalanced journal entries. Find the discrepancy:
    SELECT table_name, record_id, action, details FROM audit_log
    WHERE action IN ('CREATE', 'UPDATE')
    AND created_at > DATE_SUB(NOW(), INTERVAL 7 DAY)
    ORDER BY created_at DESC;
  2. Check for:
    • Missing offset entries
    • Duplicate entries
    • Entries with incorrect amounts
  3. Review each journal entry manually for balance.
  4. Correct the unbalanced entry via the journal entry editor.

P&L Showing Wrong Figures

Symptom: Profit & Loss statement shows unexpected values.

Solutions:

  1. Verify revenue entries match sales records.
  2. Verify expense entries are categorized correctly.
  3. Check for duplicate expense entries.
  4. Ensure all production costs are allocated.
  5. Verify the date range selected covers the correct period.

7. Email Issues

Emails Not Sending

Symptom: Password reset, invoice, or notification emails are not delivered.

Solutions:

  1. Check Resend API key:
    # Test the API key
    curl https://api.resend.com/domains \
    -H "Authorization: Bearer re_your_key_here"
  2. Check server logs for email errors:
    pm2 logs lgo-prod --lines 50 | grep -i email
  3. Verify the sender email is verified in Resend:
    • Log in to Resend dashboard
    • Check Domains → verify loftygoldenoil.com is verified
  4. Check RESEND_FROM_EMAIL matches a verified address.
  5. Check recipient email exists in the customers table.

Wrong Recipient

Symptom: Emails sent to incorrect email address.

Solutions:

  1. Verify customer email in the database:
    SELECT id, name, email FROM customers WHERE id = ?;
  2. Update if incorrect:
    UPDATE customers SET email = 'correct@email.com' WHERE id = ?;
  3. Check that the correct customer ID is being passed in the API call.

8. Performance Issues

Slow Page Loading

Symptom: Pages take more than 5 seconds to load.

Solutions:

  1. Check internet connection — Run a speed test.
  2. Check server load:
    # SSH into the server
    ssh opc@170.9.14.60

    # Check system resources
    top -bn1 | head -20
    free -h
    df -h
  3. Check PM2 memory usage:
    pm2 monit
    If memory is near 300MB limit, restart: pm2 restart lgo-prod
  4. Check database performance:
    mysql -u lgo -p -e "SHOW PROCESSLIST;"
    Kill long-running queries: mysql -u lgo -p -e "KILL <process_id>;"
  5. Enable slow query log and investigate queries taking > 2 seconds.
  6. Check Nginx access logs for slow response times:
    tail -100 /var/log/nginx/access.log | awk '{print $NF}' | sort -n

Slow API Responses

Symptom: Specific API endpoints are slow.

Solutions:

  1. Check for missing database indexes:
    SHOW INDEX FROM inventory;
    EXPLAIN SELECT * FROM inventory WHERE name LIKE '%palm%';
  2. Add indexes for frequently queried columns.
  3. Check for N+1 query patterns in the code.
  4. Verify connection pool isn't exhausted:
    SHOW STATUS LIKE 'Threads_connected';

9. Server Issues

502 Bad Gateway

Symptom: Nginx returns 502 Bad Gateway.

Cause: The Express backend (PM2) is not running or not responding.

Solutions:

  1. Check PM2 status:
    pm2 status
  2. If lgo-prod is stopped or errored:
    pm2 restart lgo-prod
  3. If PM2 shows the process as "errored":
    pm2 logs lgo-prod --lines 50
    Fix the error, then restart.
  4. Verify port 4000 is listening:
    ss -tlnp | grep 4000

503 Service Unavailable

Symptom: Nginx returns 503 Service Unavailable.

Cause: Server is overloaded or PM2 has hit its restart limit.

Solutions:

  1. Check system resources:
    top -bn1 | head -20
    free -h
  2. Check PM2 restart count:
    pm2 status
    If max restarts (10) reached, reset: pm2 reset lgo-prod && pm2 restart lgo-prod
  3. Check for memory pressure — the VPS has 4GB RAM shared with MySQL and Nginx.
  4. Consider restarting MySQL if it's consuming too much memory:
    sudo systemctl restart mysqld

Connection Refused

Symptom: Cannot connect to the server at all.

Solutions:

  1. Verify the server is running:
    ssh opc@170.9.14.60
    pm2 status
  2. Check if Nginx is running:
    sudo systemctl status nginx
  3. Check if the port is bound:
    ss -tlnp | grep -E '(443|8443|4000)'
  4. Check Oracle Cloud security list — ensure ports 80, 443, and 8443 are open in the VCN.
  5. Check firewall:
    sudo firewall-cmd --list-all

10. Database Issues

Connection Refused

Symptom: "Error: connect ECONNREFUSED 127.0.0.1:3306"

Solutions:

  1. Check if MySQL is running:
    sudo systemctl status mysqld
  2. Start MySQL if stopped:
    sudo systemctl start mysqld
  3. Check MySQL error log:
    sudo tail -50 /var/log/mysql/mysqld.log
  4. Verify the port is listening:
    ss -tlnp | grep 3306

Too Many Connections

Symptom: "Error: ER_CON_COUNT_ERROR: Too many connections"

Solutions:

  1. Check current connections:
    SHOW STATUS LIKE 'Threads_connected';
    SHOW PROCESSLIST;
  2. Kill idle connections:
    -- Kill connections idle for more than 300 seconds
    SELECT CONCAT('KILL ', id, ';') FROM information_schema.processlist
    WHERE command = 'Sleep' AND time > 300;
  3. Increase max connections temporarily:
    SET GLOBAL max_connections = 100;
  4. Check the application connection pool — reduce connectionLimit if needed.
  5. Restart MySQL to clear all connections:
    sudo systemctl restart mysqld
    pm2 restart lgo-prod

Slow Queries

Symptom: Queries taking more than 2 seconds.

Solutions:

  1. Enable the slow query log:
    SET GLOBAL slow_query_log = 'ON';
    SET GLOBAL long_query_time = 2;
  2. Find slow queries:
    mysqldumpslow -s t /var/log/mysql/slow.log
  3. Add indexes for frequently queried columns:
    CREATE INDEX idx_inventory_name ON inventory(name);
    CREATE INDEX idx_sales_customer ON sales(customer_id);
    CREATE INDEX idx_purchases_date ON purchases(purchase_date);
  4. Use EXPLAIN to analyze query plans:
    EXPLAIN SELECT * FROM inventory WHERE name LIKE '%palm%';
  5. Avoid SELECT * — select only needed columns.
  6. Use LIMIT for large result sets.

11. SSL Issues

Certificate Expired

Symptom: Browser shows "Your connection is not private" or "NET::ERR_CERT_DATE_INVALID."

Solutions:

  1. Check certificate expiry:
    openssl x509 -enddate -noout -in /etc/letsencrypt/live/loftygoldenoil.com/fullchain.pem
  2. Renew the certificate:
    sudo certbot renew
    sudo systemctl reload nginx
  3. If certbot renewal fails, check the certbot timer:
    sudo systemctl status certbot-renew.timer
    sudo journalctl -u certbot-renew
  4. Force a new certificate if needed:
    sudo certbot delete --cert-name loftygoldenoil.com
    sudo certbot certonly --nginx -d loftygoldenoil.com -d www.loftygoldenoil.com

Mixed Content Warnings

Symptom: Browser shows "Mixed Content" warning; some resources load over HTTP.

Solutions:

  1. Check all URLs in the application are HTTPS:
    • API base URL in api.js
    • Image URLs in components
    • CSS/JS asset URLs
  2. Check the FRONTEND_URL environment variable is set to https://....
  3. Search the codebase for http:// references:
    grep -r "http://" client/src/ --include="*.jsx" --include="*.js"
  4. Replace all http:// with https:// or use protocol-relative URLs (//).

12. Common Error Messages

Error MessageCauseSolution
Invalid username or passwordWrong credentialsReset password via email or admin panel
Access token requiredNo JWT in request headerLog in again to get a fresh token
Invalid or expired tokenJWT expired or tamperedLog in again; check JWT_SECRET consistency
Access denied. Required roles: [...]User role insufficientContact admin for role upgrade
Internal server errorUnhandled server errorCheck PM2 logs: pm2 logs lgo-prod
Request failed with status code 400Bad request / validation errorCheck request payload and required fields
Request failed with status code 404Resource not foundVerify the resource ID exists in the database
Request failed with status code 409Duplicate recordCheck for existing record with same unique field
Request failed with status code 413Payload too largeReduce file/payload size (limit: 15MB)
Request failed with status code 429Rate limitedWait and retry; no rate limiting currently configured
Network ErrorServer unreachableCheck server status, internet connection, and firewall
ER_CON_COUNT_ERRORToo many DB connectionsRestart MySQL and application; check pool size
ER_DUP_ENTRYDuplicate database entryCheck unique constraints; verify data isn't already saved
ECONNREFUSEDDatabase/server not runningStart MySQL or PM2 process
ETIMEOUTRequest timed outCheck server load; increase timeout or optimize query
JWT_SECRET is not definedMissing env variableAdd JWT_SECRET to server/.env
Cannot read properties of undefinedFrontend null referenceCheck API response format; verify data structure
Warning: Each child in a list should have a unique "key" propReact key warningAdd unique key prop to mapped elements
hydratation mismatchServer/client render mismatchEnsure consistent rendering; check for browser extensions

Debugging Steps for Any Issue

  1. Check browser console (F12 → Console) for JavaScript errors.
  2. Check network tab (F12 → Network) for failed API calls.
  3. Check PM2 logs (pm2 logs lgo-prod) for server-side errors.
  4. Check database for data integrity issues.
  5. Check Nginx logs (/var/log/nginx/error.log) for proxy/connection errors.
  6. Restart services in order: MySQL → PM2 → Nginx.