Skip to content

Troubleshooting — Installation

Problems standing up a new environment and getting its first users working. The procedure itself is in Installation; this page covers what goes wrong with it.

Startup fails

dotnet run, or the first request to IIS, fails before any page renders.

Error Cause Fix
NullReferenceException in Startup.ConfigureServices The StorageConfigs section is missing. Startup reads StorageConfigs.Region while registering S3. Add the whole section — see Configuration.
SqlException — cannot open database, or login failed A connection string is wrong, or a database does not exist. Check HrConnection, WareConnection and HangfireConnection. All three databases must exist.
Hangfire error at startup The Hangfire database does not exist. Hangfire creates its schema (PrepareSchemaIfNecessary = true) but not the database. CREATE DATABASE for the Hangfire store, then restart.

A wrong AWS key does not stop startup. The S3 client is built on first use, so bad credentials show up at the first upload or download instead. Under IIS, a startup failure appears as HTTP 500.30 — see Deployment for how to read it.

Nobody can sign in

On a freshly migrated database this is expected. Migrations create schema only: there is no seed data, no bootstrap script and no self-registration (R5-1). Use Route A or Route B.

If you took Route B (inserting the rows by hand) and sign-in still fails:

Symptom Cause Check
Invalid object name 'AspNetUsers' in your script The users table is dbo.ApplicationUsers; OnModelCreating remaps it. Insert into ApplicationUsers.
"Wrong username or password" with the right password The hash is not BCrypt. ApplicationUserManager replaces the Identity hasher with BCryptPasswordHasher. Generate the hash with BCrypt at work factor 12.
The same message, and the hash is right ApplicationUsers.IsEnableLogin = 0, so the password is never accepted. SELECT IsEnableLogin FROM ApplicationUsers WHERE UserName = '…'
NullReferenceException in AccountController.Login There is no visible AppUser row for this login: it is missing, or has Status <> 1 or IsDeleted = 1, so the global query filter hides it. Login then dereferences it without a null check (R6-8). SELECT UserId, Status, IsDeleted FROM AppUser WHERE IdentityUserId = '…' must return 1 and 0.
Sent to Security to change the password IsPasswordDefault = 1. Expected. Change it, or insert 0.

Wrong passwords never lock an account

Login checks the password with CheckPasswordAsync, which does not increment the failure counter, so lockout never engages (R6-7). Each failure on a known user sends that user a "Security Alert" e-mail (R6-2), if mail is working.

Signed in, but every page is unauthorised

The user reaches the layout, but the top menu is empty and gated pages redirect to /Error/UnAuthorised. Permission keys are re-read from the database on every request, so the cause is always in these rows:

Check Why
ApplicationUsers.IsEnableLogin = 1 When 0, PermissionsHandler returns an empty key list: the user stays signed in but can open nothing.
An AppUserGroupMember row with IsEnable = 1 A disabled membership contributes no keys.
AppUserGroupPermission rows linking that group to AppPermission rows The group needs the keys themselves; 219 are in force.
The spelling and case of AppPermission.PermissionName The gate compares keys case-sensitively; the menu compares them case-insensitively (R8-3). A key stored with different case shows the menu item and then refuses the page.

If a user has the keys but their data scope for an area is Default, the screen opens with an empty list. That is data scoping, not a failure — see Permissions.

General Settings crashes

On a new database, /Admin/GeneralSettings shows a NullReferenceException, and saving reports "Operation Failed!".

Bsettings expects 21 CommonSetting rows to exist already and never inserts them. GetSettings reads them by list position, and SaveSettings updates them by SettingId (R5-5). Insert them by SQL in this order, so the identity column assigns ids 1–21:

Id Setting Id Setting Id Setting
1 Cash box 8 Director approval 15 IT manager
2 Timesheet FO 9 HR approval 16 Project focal points (JSON)
3 Timesheet payer 10 Travel FO 17 Administration manager
4 Emergency cash box 11 Payroll allocator 18 Complaint admin
5 RFQ allower 12 Finance data officer 19 Supply-chain manager
6 E-mail service on/off 13 Travel safety officer 20 HR manager (saved, never read)
7 Procurement manager 14 ips — attendance allow-list 21 Compliance admin

SettingName and SettingValue are both NOT NULL. Row 14 must be named ips, because it is also looked up by name. Row 6's value must be True or False. Once the rows exist, set the real values from the screen.

Row 6 controls all e-mail

Until row 6 exists and is True, no e-mail is sent at all — see Integrations.

Approvals go nowhere

A request is submitted but appears in nobody's queue, or keeps waiting on someone who has left.

Approval chains resolve the next approver from four sources. When a lookup finds nobody, no error is raised; the document just has no usable approver.

Approver Resolved from Common cause of "nobody"
HR manager, finance manager, general manager, supply-chain director Whoever holds a position named exactly HR and Admin Manager, Finance Manager, General Manager or Supply Chain Director (R6-10) The position was renamed or spelt differently, or two positions share the name — the lookup uses SingleOrDefault and swallows the error. The "HR Manager" field in General Settings has no effect here.
Line manager AppUser.ManagerId on the requester's employee record, set in /HumanResource/EditEmp Not set; or it still points to someone who has left. Nothing re-points it, so requests keep routing to the old manager.
Branch manager, HR, finance, logistics, treasury, accounting The branch record, set at /Admin/EditBranch Not set for the requester's branch.
Allocator, procurement manager, HR approval, director approval, timesheet and travel officers General Settings rows The row is missing, or holds the id of someone who has left.

After fixing the data, re-point open documents with Refresh Next users at /Admin/TriggerNextUser, choosing All. If anyone has run it for a single type since the last restart, only that type is refreshed until the app pool recycles (R8-11), so recycle first if in doubt.

Some approvals also need the approver to have an approved e-signature: procurement, warehouse, leave, advance and overtime approvals check BSign.HasSign, and so does check-in from the home page. An approver without one is refused at that step.