Troubleshooting Guide
Lofty Golden Oil ERP — Diagnosing and Resolving Common Issues
Table of Contents
- Login Issues
- Navigation Issues
- Inventory Issues
- Sales Issues
- Production Issues
- Accounting Issues
- Email Issues
- Performance Issues
- Server Issues
- Database Issues
- SSL Issues
- Common Error Messages
1. Login Issues
Wrong Password
Symptom: "Invalid username or password" error after entering credentials.
Solutions:
- Verify Caps Lock is off and password is typed correctly.
- Use the "Forgot Password" link on the login page to reset via email.
- As admin, reset the password directly:
Generate a new bcrypt hash in Node.js:mysql -u lgo -p lgo_erp_prodUPDATE users SET password = '$2a$10$...' WHERE username = 'affected_user';const bcrypt = require('bcryptjs');console.log(await bcrypt.hash('new_password', 10));
- Verify the
RESEND_API_KEYis valid if password reset emails are not arriving.
Account Locked / Inactive
Symptom: Login fails with no error message or "Account is inactive."
Solutions:
- Check user status in the database:
SELECT id, username, status FROM users WHERE username = 'affected_user';
- If status is
inactive, reactivate:UPDATE users SET status = 'active' WHERE username = 'affected_user'; - Contact an administrator to restore access.
Browser Cache Issues
Symptom: Login page loops, shows stale data, or behaves unexpectedly.
Solutions:
- 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
- Clear browser cache:
Cmd+Shift+Delete(macOS) orCtrl+Shift+Delete(Windows). - Open an incognito/private window and try logging in.
- Try a different browser.
JWT Expired
Symptom: Suddenly logged out mid-session, or "Access denied" / "Token expired" errors.
Solutions:
- Log in again — JWT tokens expire after 24 hours.
- If this happens frequently, check that the server clock is correct:
timedatectl status
- If using a proxy, ensure
Authorizationheaders are forwarded correctly. - Check that
JWT_SECREThas 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:
- Role-based visibility: Menu items are filtered by user role. Verify your role:
SELECT username, role FROM users WHERE username = 'your_username';
- Available roles and their modules:
Role Visible Modules adminAll modules productionDashboard, Inventory, Production, Requisitions salesDashboard, Inventory, Sales, Customers, Requisitions accountantDashboard, Expenses, AR, Reports, Requisitions - 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:
- Open browser Developer Tools (
F12) → Console tab. Check for JavaScript errors. - Common causes:
- JavaScript bundle failed to load (network issue)
- API returned unexpected data format
- Component rendering error
- Check the browser Console for specific error messages.
- Verify the frontend build is current:
ls -la /var/www/lgo-erp/client/dist/
- Rebuild if necessary:
cd /home/opc/lofty-golden-oil-erpnpm run build
Theme Issues
Symptom: Dark/light mode not applying correctly, colors look wrong, text unreadable.
Solutions:
- Toggle the theme using the theme switcher in the header.
- Check that
index.cssCSS variables are correctly defined. - Clear browser cache — stale CSS may be served.
- Verify
tailwind.config.jsincludesdarkMode: 'class'. - Check if the
<html>element has thedarkclass:document.documentElement.classList.contains('dark')
3. Inventory Issues
Stock Not Updating
Symptom: Purchases or sales are recorded but stock quantity hasn't changed.
Solutions:
- Verify the purchase/sale was actually saved:
SELECT * FROM purchases WHERE id = ?;SELECT * FROM sales WHERE id = ?;
- Check if stock updates are applied in the correct database transaction.
- Verify the product ID matches between the purchase/sale and inventory records.
- Check for pending inventory adjustments.
- Refresh the page — the data may not have re-fetched.
Wrong Stock Count
Symptom: Inventory quantity doesn't match physical count.
Solutions:
- Check for manual adjustments:
SELECT * FROM inventory_adjustments WHERE product_id = ? ORDER BY created_at DESC;
- Verify all purchases have been received and recorded.
- Verify all sales have been recorded with correct quantities.
- Check for production runs that consumed raw materials.
- 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:
- Check the product's
low_stock_thresholdvalue:SELECT name, quantity, low_stock_threshold, expiry_date FROM inventory WHERE id = ?; - Ensure the threshold is set (default is 100 litres).
- Check that
expiry_dateis set for expiry-related alerts. - 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:
- Customer must be active:
If status isSELECT id, name, status FROM customers WHERE id = ?;
inactive, reactivate or select a different customer. - Product must be in stock:
Quantity must be greater than 0 and status must beSELECT id, name, quantity, status FROM inventory WHERE id = ?;
active. - All required fields must be filled: Customer, product, quantity, and price.
- Quantity cannot exceed available stock.
- Check browser console for API error responses.
Invoice Not Generating
Symptom: Sale is recorded but no invoice is created.
Solutions:
- Check the sale status — invoice generation may be triggered by a specific status.
- Verify the AR invoices endpoint:
curl http://localhost:4000/api/ar/invoices -H "Authorization: Bearer $TOKEN"
- Check the database for the invoice record:
SELECT * FROM ar_invoices WHERE sale_id = ?;
- 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:
- Check printer connection and paper supply.
- Ensure the browser allows pop-ups for the site (print often opens a new window).
- Try
Cmd+P/Ctrl+Pto print the current page manually. - Clear browser cache if the print template is stale.
- 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:
- Verify input volume and output volume are correct:
SELECT input_volume, output_volume, yield_percentage FROM production_runs WHERE id = ?;
- Yield % formula:
(output_volume / input_volume) × 100 - Check for data entry errors (e.g., litres vs. kilograms).
- Verify all production runs for the batch are accounted for.
Cost Per Litre Wrong
Symptom: Production cost per litre doesn't match expectations.
Solutions:
- Cost per litre formula:
total_input_cost / output_volume - Verify input costs are correct:
SELECT p.*, i.price FROM production_runs prJOIN purchases p ON pr.purchase_id = p.idWHERE pr.id = ?;
- Check for shared costs (labour, overhead) that should be allocated.
- 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:
- This indicates unbalanced journal entries. Find the discrepancy:
SELECT table_name, record_id, action, details FROM audit_logWHERE action IN ('CREATE', 'UPDATE')AND created_at > DATE_SUB(NOW(), INTERVAL 7 DAY)ORDER BY created_at DESC;
- Check for:
- Missing offset entries
- Duplicate entries
- Entries with incorrect amounts
- Review each journal entry manually for balance.
- Correct the unbalanced entry via the journal entry editor.
P&L Showing Wrong Figures
Symptom: Profit & Loss statement shows unexpected values.
Solutions:
- Verify revenue entries match sales records.
- Verify expense entries are categorized correctly.
- Check for duplicate expense entries.
- Ensure all production costs are allocated.
- 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:
- Check Resend API key:
# Test the API keycurl https://api.resend.com/domains \-H "Authorization: Bearer re_your_key_here"
- Check server logs for email errors:
pm2 logs lgo-prod --lines 50 | grep -i email
- Verify the sender email is verified in Resend:
- Log in to Resend dashboard
- Check Domains → verify
loftygoldenoil.comis verified
- Check RESEND_FROM_EMAIL matches a verified address.
- Check recipient email exists in the customers table.
Wrong Recipient
Symptom: Emails sent to incorrect email address.
Solutions:
- Verify customer email in the database:
SELECT id, name, email FROM customers WHERE id = ?;
- Update if incorrect:
UPDATE customers SET email = 'correct@email.com' WHERE id = ?;
- 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:
- Check internet connection — Run a speed test.
- Check server load:
# SSH into the serverssh opc@170.9.14.60# Check system resourcestop -bn1 | head -20free -hdf -h
- Check PM2 memory usage:
If memory is near 300MB limit, restart:pm2 monit
pm2 restart lgo-prod - Check database performance:
Kill long-running queries:mysql -u lgo -p -e "SHOW PROCESSLIST;"
mysql -u lgo -p -e "KILL <process_id>;" - Enable slow query log and investigate queries taking > 2 seconds.
- 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:
- Check for missing database indexes:
SHOW INDEX FROM inventory;EXPLAIN SELECT * FROM inventory WHERE name LIKE '%palm%';
- Add indexes for frequently queried columns.
- Check for N+1 query patterns in the code.
- 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:
- Check PM2 status:
pm2 status
- If
lgo-prodis stopped or errored:pm2 restart lgo-prod - If PM2 shows the process as "errored":
Fix the error, then restart.pm2 logs lgo-prod --lines 50
- 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:
- Check system resources:
top -bn1 | head -20free -h
- Check PM2 restart count:
If max restarts (10) reached, reset:pm2 status
pm2 reset lgo-prod && pm2 restart lgo-prod - Check for memory pressure — the VPS has 4GB RAM shared with MySQL and Nginx.
- 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:
- Verify the server is running:
ssh opc@170.9.14.60pm2 status
- Check if Nginx is running:
sudo systemctl status nginx
- Check if the port is bound:
ss -tlnp | grep -E '(443|8443|4000)'
- Check Oracle Cloud security list — ensure ports 80, 443, and 8443 are open in the VCN.
- Check firewall:
sudo firewall-cmd --list-all
10. Database Issues
Connection Refused
Symptom: "Error: connect ECONNREFUSED 127.0.0.1:3306"
Solutions:
- Check if MySQL is running:
sudo systemctl status mysqld
- Start MySQL if stopped:
sudo systemctl start mysqld
- Check MySQL error log:
sudo tail -50 /var/log/mysql/mysqld.log
- Verify the port is listening:
ss -tlnp | grep 3306
Too Many Connections
Symptom: "Error: ER_CON_COUNT_ERROR: Too many connections"
Solutions:
- Check current connections:
SHOW STATUS LIKE 'Threads_connected';SHOW PROCESSLIST;
- Kill idle connections:
-- Kill connections idle for more than 300 secondsSELECT CONCAT('KILL ', id, ';') FROM information_schema.processlistWHERE command = 'Sleep' AND time > 300;
- Increase max connections temporarily:
SET GLOBAL max_connections = 100;
- Check the application connection pool — reduce
connectionLimitif needed. - Restart MySQL to clear all connections:
sudo systemctl restart mysqldpm2 restart lgo-prod
Slow Queries
Symptom: Queries taking more than 2 seconds.
Solutions:
- Enable the slow query log:
SET GLOBAL slow_query_log = 'ON';SET GLOBAL long_query_time = 2;
- Find slow queries:
mysqldumpslow -s t /var/log/mysql/slow.log
- 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);
- Use
EXPLAINto analyze query plans:EXPLAIN SELECT * FROM inventory WHERE name LIKE '%palm%'; - Avoid
SELECT *— select only needed columns. - Use
LIMITfor large result sets.
11. SSL Issues
Certificate Expired
Symptom: Browser shows "Your connection is not private" or "NET::ERR_CERT_DATE_INVALID."
Solutions:
- Check certificate expiry:
openssl x509 -enddate -noout -in /etc/letsencrypt/live/loftygoldenoil.com/fullchain.pem
- Renew the certificate:
sudo certbot renewsudo systemctl reload nginx
- If certbot renewal fails, check the certbot timer:
sudo systemctl status certbot-renew.timersudo journalctl -u certbot-renew
- Force a new certificate if needed:
sudo certbot delete --cert-name loftygoldenoil.comsudo 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:
- Check all URLs in the application are HTTPS:
- API base URL in
api.js - Image URLs in components
- CSS/JS asset URLs
- API base URL in
- Check the
FRONTEND_URLenvironment variable is set tohttps://.... - Search the codebase for
http://references:grep -r "http://" client/src/ --include="*.jsx" --include="*.js" - Replace all
http://withhttps://or use protocol-relative URLs (//).
12. Common Error Messages
| Error Message | Cause | Solution |
|---|---|---|
Invalid username or password | Wrong credentials | Reset password via email or admin panel |
Access token required | No JWT in request header | Log in again to get a fresh token |
Invalid or expired token | JWT expired or tampered | Log in again; check JWT_SECRET consistency |
Access denied. Required roles: [...] | User role insufficient | Contact admin for role upgrade |
Internal server error | Unhandled server error | Check PM2 logs: pm2 logs lgo-prod |
Request failed with status code 400 | Bad request / validation error | Check request payload and required fields |
Request failed with status code 404 | Resource not found | Verify the resource ID exists in the database |
Request failed with status code 409 | Duplicate record | Check for existing record with same unique field |
Request failed with status code 413 | Payload too large | Reduce file/payload size (limit: 15MB) |
Request failed with status code 429 | Rate limited | Wait and retry; no rate limiting currently configured |
Network Error | Server unreachable | Check server status, internet connection, and firewall |
ER_CON_COUNT_ERROR | Too many DB connections | Restart MySQL and application; check pool size |
ER_DUP_ENTRY | Duplicate database entry | Check unique constraints; verify data isn't already saved |
ECONNREFUSED | Database/server not running | Start MySQL or PM2 process |
ETIMEOUT | Request timed out | Check server load; increase timeout or optimize query |
JWT_SECRET is not defined | Missing env variable | Add JWT_SECRET to server/.env |
Cannot read properties of undefined | Frontend null reference | Check API response format; verify data structure |
Warning: Each child in a list should have a unique "key" prop | React key warning | Add unique key prop to mapped elements |
hydratation mismatch | Server/client render mismatch | Ensure consistent rendering; check for browser extensions |
Debugging Steps for Any Issue
- Check browser console (
F12→ Console) for JavaScript errors. - Check network tab (
F12→ Network) for failed API calls. - Check PM2 logs (
pm2 logs lgo-prod) for server-side errors. - Check database for data integrity issues.
- Check Nginx logs (
/var/log/nginx/error.log) for proxy/connection errors. - Restart services in order: MySQL → PM2 → Nginx.