Troubleshooting the New User Interface
The problems below account for almost everything that goes wrong when setting the interface up for the first time. Work through the checks in order, they are ordered by how often each turns out to be the cause.
The interface is not reachable at all
/user/ shows the classic application's "not found" page, or the classic sign-in page.
Check the setting is actually
true.bashgrep '^UI_ENABLED' .oempro_envRecreate the containers.
bash./cli/octeth.sh docker:updocker:uprecreates the reverse proxy and the interface wheneverUI_ENABLEDchanges. If the interface still answers "not found" afterwards,docker ps --filter name=oempro_uishows it asunhealthy; see the next section.Check the container is running.
bashdocker ps --filter name=oempro_ui
The interface container is unhealthy
With UI_ENABLED=true, the oempro_ui container reports healthy only when the interface answers its health page. unhealthy means it is running but serving "not found" for every page. ./cli/octeth.sh health:check reports the same problem under NewUserInterface.
Read why it is not serving.
bashdocker logs oempro_ui 2>&1 | grep -E "UI_ENABLED is not true|ERROR" | head -20UI_ENABLED is not truemeans the container started before the flag was set. Run./cli/octeth.sh docker:up, ordocker restart oempro_ui.An
ERRORaboutui/.envmeans a value in.oempro_envcannot be read by the interface. The log names the setting without printing its value. Fix it, then:bashdocker restart oempro_ui
The first start with the interface on installs its dependencies, so the container can take a few minutes to become healthy. That is expected.
Every page shows a server error
Usually a blank page or a plain "server error" message, on every page including sign-in.
Build the assets.
./cli/octeth.sh ui:buildThe stylesheets and scripts are built at release time and shipped inside the release package. An install made from a source checkout has none, and every page fails to render without them.
TIP
If the build succeeds and pages still fail, check the container's log for the first error rather than the last:
docker logs oempro_ui 2>&1 | head -50The container starts but says the database is missing
The log shows a message naming the database and pointing at a command.
Create it.
./cli/octeth.sh ui:db-setupThe interface has its own database and no permission to create one, because it is deliberately never given administrator credentials for MySQL. The installer and the upgrade script normally create it for you, so seeing this means neither has run.
The container refuses to start and names Octeth's database
The log says the interface's database is Octeth's own database and it is refusing to start.
This is a safety measure doing its job. Set UI_MYSQL_DATABASE in .oempro_env to a separate name, oempro_ui by convention, then create it with ./cli/octeth.sh ui:db-setup.
DANGER
Do not work around this by making the two match. The interface's test suite rebuilds whatever database it is pointed at from scratch. Pointed at Octeth's own database, that destroys the entire install.
The logo or the tab icon is broken
Check the path starts with /ui/.
UI_BRAND_LOGO_MARK=/ui/images/brand/acme-mark.svgThe reverse proxy only forwards a fixed set of paths to the interface, and /ui/ is the one carrying its files. An image at /images/acme-mark.svg is handed to the classic application instead, which does not have it.
TIP
Confirm by opening the image address directly in a browser. If it displays or downloads, the path is right.
The same applies to the tab icon. A file at /favicon.ico is answered by the classic application, so the icon must also sit under /ui/.
A colour change did nothing
Check the value is quoted.
iniUI_BRAND_ACCENT="#2563EB"An unquoted
#is read as the start of a comment. The result is a value that looks correct in the file and is empty when read.Check the value is a valid hex colour of 3, 6 or 8 digits including the
#. Anything else is ignored and the built-in colour is used, so a typo shows up as a colour that did not change rather than as an error.If the sidebar or the signed-out panel did not change, that is expected. Those two have dark backgrounds and render their highlights in white regardless of your accent, because a dark accent would be invisible against them.
Charts still use the old colours
Expected. Charts use several colours to tell one data series from another, so they keep their own palette. Collapsing them to a single brand colour would make the series impossible to tell apart. The same applies to the interface's own emails and the invoice PDF, which carry fixed colours because email clients cannot read the mechanism that applies your palette to a web page.
The staff billing screens return "not found"
Billing is turned off. Set it on and recreate the containers:
BRAND_FEATURE_BILLING=trueWARNING
The setting does not start with UI_. UI_BRAND_FEATURE_BILLING has no effect.
A billing command runs and produces no output
Same cause. Every billing command deliberately does nothing when billing is off.
A customer cannot be placed in a user group
The reconcile job reports an error for that account, or a plan change fails.
- Check the plan has a Default group link on the Catalog screen. Every plan needs one.
- Check the account-wide groups under Staff, Billing settings, User groups.
- Open the Billing health screen, which lists exactly which links are missing.
Nobody is ever suspended for not paying
The suspended user group is not set. Set it under Staff, Billing settings, User groups, or give the plan its own suspended link on the Catalog screen. Payments keep working without it, which is why this can go unnoticed for a long time.
Payments succeed but Octeth does not update
The webhook is not arriving.
- Check the address in your processor's dashboard includes
/ui/, for examplehttps://your-domain/ui/webhooks/stripe. - Check the signing secret saved on the Payment gateways screen matches the one your processor shows.
- Open the Billing health screen and look at the webhook section for recent deliveries.
- If refunds issued in the Stripe dashboard do not appear on the customer's page, or a staff refund stays at Refund pending confirmation, check that the Stripe webhook endpoint receives the refund events. See Step 4 of Connecting a Payment Gateway.
Password reset emails never arrive
The interface writes its emails to a log by default rather than sending them. Set a real mail server:
UI_MAIL_MAILER=smtp
UI_MAIL_HOST=email-smtp.eu-west-1.amazonaws.com
UI_MAIL_PORT=587
UI_MAIL_USERNAME=AKIAEXAMPLE
UI_MAIL_PASSWORD="your smtp password"
UI_MAIL_FROM_ADDRESS=no-reply@acmemail.comEvery hosted mail service needs UI_MAIL_USERNAME and UI_MAIL_PASSWORD. Leave them empty only for a mail server that accepts this server by its IP address. Port 587 and port 465 both work without setting UI_MAIL_SCHEME.
Then run docker compose up -d --force-recreate oempro_ui.
If a mail server is set and the emails still do not arrive, look in the interface's log for Password reset: the reset email could not be sent. The line names the error the mail server returned. The person asking for the reset still sees the usual "sent" screen, on purpose, so the page never reveals which addresses have accounts.
The signup link is missing, or the signup page says signup is not available
Self-signup needs a working mail server. A new account is enabled only by the link in its welcome email, so the interface offers signup only while UI_MAIL_MAILER sends for real (smtp, sendmail, ses, postmark or resend). Under log, the default, or any other value, it does not offer signup at all. Set up mail as described in Password reset emails never arrive.
If mail is set up, check that self-signup is switched on in Admin > Settings > ESP settings. The interface reads that setting every minute, so no restart is needed.
A new customer was told their verification email could not be sent
The mail server rejected the welcome email. The account was created but is disabled until it is verified. Fix the mail settings using the line Signup: the account was created but its verification email could not be sent in the interface's log, which names the error and the account's user ID. Then enable that account in the legacy admin area. The customer cannot sign up again with the same address, because the account already exists.
The interface shows numbers that are clearly fictional
Demo mode is on. Set UI_DEMO_MODE=false in .oempro_env and restart the interface container. Demo mode replaces the live connection with a fictional dataset for screenshots, and it looks entirely normal while it is on.
INFO
Demo mode only takes effect when APP_ENV is local or testing, so this cannot happen on a production install.
Everyone was signed out and nothing decrypts
UI_APP_KEY changed. If you still have the old value, put it back and restart. If it is gone, everyone signs in again and any secrets the interface had encrypted, such as saved payment gateway credentials, have to be entered again.
TIP
Include .oempro_env in your backups. It holds this key.
Some numbers on a list screen look wrong
A small number of screens in the list area are not yet connected to live data and show placeholder figures. If a figure looks implausible on a list screen and everything else on the account is consistent, this is the likely explanation rather than a data problem. This is known and is being worked on.
Getting more detail
Turn the log level up temporarily:
UI_LOG_LEVEL=debugRestart the interface container, reproduce the problem, then read the log:
docker logs oempro_ui 2>&1 | tail -100WARNING
Set it back to error afterwards. Debug logging is verbose and can fill a disk on a busy install.
Masked values in the logs
New in v6.0.1 Every log channel of the interface (laravel.log, octeth.log, and stderr, syslog, Slack or Papertrail when configured) masks credentials before a record is written: in log messages, in logged context values and in exception messages. A masked value appears as <redacted:N>, where N is its length. Values under a sensitive key such as SessionID, Password, or any key ending in token or secret are masked whole, and so is any unbroken run of 32 or more key-like characters, which includes UUIDs. Stack traces are not changed. Records written by the emergency fallback logger, which Laravel uses when a configured channel cannot be built, are masked the same way.A file path stays readable when it starts at a standard filesystem root (/var/, /tmp/, /usr/, /home/, /opt/, /etc/, /proc/, /srv/, /run/) and none of its segments is 32 characters or longer. Any other long path is masked like any other long value. If you search the logs for a full session ID, API key or UUID, search for the masked form instead.

