Repository navigation
Troubleshooting
| Question | File |
|---|---|
| What happened in the most recent run? | C:\ProgramData\ManagedState\reports\run.log |
| What is the state of every payload? | C:\ProgramData\ManagedState\reports\items.json |
| What happened in a specific run? | C:\ProgramData\ManagedState\logs\<date>\<HHmm>\startset.log |
| Structured events for a run | ...\<HHmm>\events.jsonl |
| Warnings and errors, machine-wide | Application event log, source StartSet
|
| Why did an MSI payload fail? | C:\ProgramData\ManagedState\logs\installs\<name>_<timestamp>.log |
Get-Content C:\ProgramData\ManagedState\reports\items.json | ConvertFrom-Json |
Where-Object current_status -ne 'Installed' |
Format-Table item_name, current_status, last_errorRaise verbosity by setting log_level: debug in Config.yaml and restarting the service. The
--verbose and --debug command-line switches do not change the level.
Cause: the service is not running, or was never registered.
Get-Service StartSetFix: register and start it — see Installation. If it starts and immediately
stops, check the Application event log for the fatal exception; the service logs
StartSet Service failed to start and rethrows.
Causes, in order of likelihood:
-
It is in a subdirectory. Discovery is top level only. A file at
login-every\MyTool\Setup.ps1is invisible. -
Its extension is not in
allowed_extensions..psm1,.py,.vbs,.regand anything else are ignored silently. Only.ps1,.cmd,.bat,.exe,.msi,.msixby default. -
It is a
*-oncepayload that already ran. Check withmanagedstatekeeper list --type <type> --show-executed.
Fix for 1 and 2: move the file flat into the directory with a supported extension. Fix for 3: edit the script (the checksum change re-arms it) or clear the ledger entry.
Cause: it is login-privileged-once, which is tracked in runonce-system.json — once per
machine, not per user.
Fix: move genuinely per-user work to login-once, or make it idempotent and use
login-privileged-every. See Coming From Outset.
Cause: the Security event log is not being watched. The worker logs
Insufficient permissions to monitor Security event log. Login scripts will not be triggered automatically. This happens when the service is not running as LocalSystem.
Get-CimInstance Win32_Service -Filter "Name='StartSet'" | Select-Object StartNameFix: the service must run as LocalSystem. Re-register it, or correct the account:
sc.exe config StartSet obj= LocalSystemAlso confirm logon success auditing is enabled — the watcher needs 4624 events to exist:
auditpol /get /subcategory:"Logon"Cause: this is the classic symptom of a logon watcher that only sees logon types 2 and 10. StartSet watches 2, 10 and 11 precisely for this reason, so if you see it, the running binary is older than that fix.
Fix: update to a current build. Confirm the running version:
& 'C:\Program Files\StartSet\managedstatekeeper.exe' --versionCause: it ran as SYSTEM in session 0 — HKCU went to SYSTEM's hive, user32 calls did
nothing, or a "skip administrators" guard matched SYSTEM and the script exited 0.
Current builds do not do this: user-context payloads either run in the user's session or are
marked Deferred. If you see it, either the payload is in a privileged directory (which is
SYSTEM by design), or it is an .msi/.msix (packages are never routed into the user session), or
the binary predates the user-context dispatch.
Fix: move per-user work into login-once, login-every or on-demand, and keep it as a
script rather than a package.
Cause: the console user's session could not be reached. The log line names the reason —
no active console session or a WTSQueryUserToken failed message.
Common reasons:
| Reason | Explanation |
|---|---|
no active console session |
Nobody is signed in at the console, or the run was driven from a remote/non-interactive context |
WTSQueryUserToken failed … |
The token did not exist yet, or the caller is not LocalSystem
|
… still unavailable after 60s |
The full token wait elapsed with no token |
… not waiting again; an earlier payload in this run already waited 60s |
A previous payload in the same run exhausted the wait |
Fix: the condition is usually transient — the next sign-in retries. If it is persistent,
confirm the service runs as LocalSystem, confirm a user really is signed in at the console
(not only over RDP to a different session), and do not test user-context payloads from a remote
shell.
Cause: a payload in an elevated directory is not owned by SYSTEM, the built-in Administrator,
Administrators, or a service SID.
Get-Acl 'C:\ProgramData\ManagedState\boot-every\Configure-Firewall.ps1' | Select-Object OwnerFix:
icacls 'C:\ProgramData\ManagedState\boot-every\Configure-Firewall.ps1' /setowner "SYSTEM"This is a security control, not a bug: it is what stops a standard user with write access to the directory from obtaining SYSTEM execution.
Cause: checksum_validation is on and the payload's hash no longer matches
share\checksums.yaml.
Fix: re-record after an intentional edit:
managedstatekeeper checksum "C:\ProgramData\ManagedState\login-every\Map-DepartmentDrives.ps1" --recordRemember the checksum entry is keyed by full path — a moved payload silently stops being covered.
Cause: wait_for_network is true, the network did not come up within network_timeout, and
ignored_network_failure is false, so the whole run aborts with
Network not available, aborting execution.
Fix: either raise network_timeout, or set ignored_network_failure: true to run anyway.
Watch the key spelling — it is ignored_network_failure, with the -ed. The mis-spelled
ignore_network_failure is silently ignored and leaves the default in place.
For an ad-hoc run, bypass it:
managedstatekeeper boot --skip-networkNote that "network available" means a non-loopback, non-tunnel interface that is Up with an IPv4 default gateway. A machine on an isolated VLAN with no gateway will never satisfy it.
Causes:
- The service was not restarted. Preferences are loaded once at start-up; the file watcher is not enabled in the service host.
- The key is misspelled. Unknown keys are ignored silently.
Fix:
Restart-Service StartSetThen confirm Loaded preferences from C:\ProgramData\ManagedState\Config.yaml in the session log,
and that the behaviour actually changed.
Cause: the payload is waiting on something — a prompt, a UI, a lock. Execution is sequential,
so one hung payload blocks everything after it until script_timeout (default one hour) expires
and the process tree is killed.
Fix: lower script_timeout, and make payloads non-interactive. PowerShell payloads are already
launched with -NonInteractive -NoProfile; batch payloads and executables are not protected in the
same way. A hung msiexec is usually another installation holding the Windows Installer mutex —
check for exit code 1618 in logs\installs\.
Cause: only ExecutionStatus.Failed — a payload that ran and returned non-zero — raises the
exit code. A timeout, a deferral, a skipped permission or checksum check, and an unsupported
extension all leave it at 0.
Fix: do not gate automation on the exit code alone. Read reports\items.json and check for
current_status values other than Installed.
msiexec exit codes are mapped in the log:
| Code | Meaning |
|---|---|
0 |
Success |
3010 |
Success, restart required — treated as success |
1601 |
Installation service not started |
1603 |
Fatal error during installation |
1618 |
Another installation is already in progress |
1619 |
Installation package could not be opened |
1620 |
Installation package path not found |
1625 |
Installation prohibited by policy |
1638 |
Another version of this product is already installed |
The verbose log is at C:\ProgramData\ManagedState\logs\installs\<name>_<timestamp>.log. Start
there; the mapped message alone is rarely enough for a 1603.
Cause: StartSet prefers PowerShell 7 (pwsh.exe) when it is installed, falling back to
Windows PowerShell 5.1. It also runs with -NoProfile -NonInteractive -ExecutionPolicy Bypass, so
nothing from your profile is loaded, and the working directory is the payload directory.
Fix: test the payload the same way:
pwsh.exe -NoProfile -NonInteractive -ExecutionPolicy Bypass -File .\MyPayload.ps1Cause: anything sitting loose in C:\ProgramData\ManagedState\logs is treated as a log and
deleted once it is 30 days old, regardless of filename.
Fix: write payload output somewhere else, or let StartSet capture it — payload stdout and
stderr are folded into the session log when log_script_output is true.
Cause: the process was killed. On a machine with a scheduled restart or a forced shutdown, an
in-flight session simply ends; session.json stays at "status": "running" with no end_time.
Fix: look at what was executing at the end of startset.log. A long-running package payload
truncated by a reboot will be retried on the next trigger, which for a *-once payload means it
was never recorded and will run again.
Force every *-once payload to run again for one user:
Remove-Item 'C:\ProgramData\ManagedState\share\runonce-jsmith.json'Force system-context *-once payloads to run again:
Remove-Item 'C:\ProgramData\ManagedState\share\runonce-system.json'Clear one payload for one user without touching the rest:
managedstatekeeper remove-override Map-DepartmentDrives.ps1 --clear-runonce --user jsmithClear stale trigger files:
managedstatekeeper cleanupFull reset of runtime state, keeping payloads:
Stop-Service StartSet
Remove-Item 'C:\ProgramData\ManagedState\share\runonce-*.json'
Remove-Item 'C:\ProgramData\ManagedState\logs' -Recurse -Force
Start-Service StartSetStartSet — MIT licensed — windowsadmins/startset — a Windows port of macadmins/outset.
StartSet
How it works
Migrating
Operating
Contributing