09. Diagnostics and Troubleshooting - intersystems/ipm GitHub Wiki

This page covers the tools IPM provides for finding out what it did, when, and why something failed.

History log

Availability: IPM v0.10.5+

IPM records every install, load, uninstall, and update it attempts, whether or not the attempt succeeded. Each record captures the full command string, the user who ran it, start and end times, the resulting %Status, the repository the package came from, and per-phase timings.

The log is written to %IPM_General.History and is scoped to the namespace it was created in. A record is created as soon as the action starts and filled in when it finishes, so a process that dies mid-install still leaves a record behind, with an empty end time and a placeholder version.

The log is durable across install failures. A failed install rolls back its transaction, which would take the log record with it. To avoid that, IPM tracks the action in an IRISTEMP twin and copies the result into %IPM_General.History outside the transaction, so failures remain visible after the rollback.

Reading the log

Command: history [flags] [<action> [<argument>]]

Alias: log

Arguments:

  • action
    One of: find (default), details, delete, schema
  • argument
    Record ID, for the details action

Flags:

  • -globally, -g
    Operate on all namespaces rather than just the current one. Defaults to false.
  • -sort <value>, -asc <value>
    Order of listed records. Allowed values are asc and desc. Defaults to descending order of ID, so the newest record appears first.
  • -limit <value>
    Show only this many records. 0 shows all. If omitted, uses DefaultLogEntryLimit, which defaults to 20.
  • -showPhases, -p, -phases
    Include lifecycle phase timings in the details view.
  • -confirm
    Required by history delete when no filter is given, to confirm deletion of every record in the namespace. Not required when -globally is passed.
  • -verbose, -v
    Produces verbose output from the command.

With no arguments, history lists the most recent records in the current namespace:

zpm:USER>history

Namespace: USER
ID Action Package Version UserName Time Success Committed CommandString Source

21 uninstall isc.json v2.0.1 irisowner 2026-04-06 15:21:49 (0 sec) Success Committed uninstall -all n/a
15 load  <unset> irisowner 2026-04-06 15:21:26 (0 sec) ERROR #6301: SAX XML Parser Error: expected end of tag 'Malformed' while processing Anonymous Stream at line 9 offset 7 Uncommitted load /home/irisowner/zpm/tests/integration_tests/Test/PM/Integration/_data/malformed-module n/a
13 install isc.rest v1.2.2 irisowner 2026-04-06 15:20:11 (12 sec) Success Committed install isc.rest registry

Columns

Column Meaning
ID Record ID, unique within the namespace. Pass it to history details.
Action One of load, install, uninstall, update.
Package Module name. May be empty if the action failed before a name was known, for example when loading a directory that has no valid module.xml.
Version Module version. Displayed as <unset> when the action failed before the version was resolved.
UserName The $username that initiated the action.
Time Start time, with elapsed seconds in parentheses. An entry with no elapsed time either is still running or ended abnormally.
Success Success if the action returned $$$OK, otherwise the error text. The underlying column stores the raw %Status.
Committed Whether the action's work was permanently written to disk. See Committed vs. Success.
CommandString The shell command that triggered the action, as typed.
Source Repository the module came from. n/a for anything other than install.

In every column, control characters are printed as <CR>, <LF>, and <TAB> so a record always occupies one line.

Committed vs. Success

These two columns answer different questions, and a record can be committed without being successful.

Success is the status the action returned. Committed is whether the action's changes survived. Normally the two agree, because a failed install rolls back its transaction and a successful one commits.

Developer mode separates them. A -dev install commits the module manifest early so it can be fixed after a failure, and deliberately runs the reload, compile, and activate phases outside a transaction so they can be debugged. A -dev action that fails in one of those phases keeps whatever it had already done, so it is recorded as Committed with an error in Success. That combination is the one to look for when a namespace is in an unexpected state.

Filtering

history find accepts -D<column>=<value> filters. Multiple filters are combined with AND. Use history schema to list the filterable column names.

Values support a wildcard and comparison operators:

Form Meaning
-DPackage=isc.rest Exact match
-DCommandString="load*" * is a wildcard, translated to SQL LIKE
-DTimeStart=">2026-01-01 00:00:00" Also >=, <, <=, <>

Quote the value when it contains spaces or a comparison operator.

history find -Daction=install -Dpackage=zpip
history find -DCommandString="load*"
history find -DCommitted=1
history find -DTimeStart=">2026-01-01 00:00:00"
history find -sort asc -limit 5

Note that Success stores a raw %Status, where 1 means OK and error statuses begin with 0. So -DSuccess="0*" finds actions that errored, and also catches records still in progress, whose status has not been set yet.

The Version property is stored as its components, so filter on Version_Major, Version_Minor, Version_Patch, Version_Prerelease, and Version_Build rather than on a version string.

Inspecting a single record

history details <id> prints one record with every field on its own line, including the expanded repository details.

zpm:USER>history details 15

ID: 15
Action: load
Package:
Version: <unset>
UserName: irisowner
Time: Started At 2026-04-06 15:21:26; Ended At 2026-04-06 15:21:26
Success: 0 HVexpected end of tag 'Malformed' while processing Anonymous Stream at line 9 offset 7ã"ParseStream+22^%XML.SAX.Parser.1USER·&e^ParseStream+22^%XML.SAX.Parser.1^2^Open+19^%XML.Reader.1^1 e^OpenStream+1^%XML.Reader.1^15e^GetModuleObjectFromStream+3^%IPM.Utils.Module.1^13e^GetModuleObjectFromPath+9^%IPM.Utils.Module.1^14e^GetModuleDefaultsFromXML+1^%IPM.Utils.Module.1^1)d^LoadNewModule+7^%IPM.Utils.Module.1^1!e^LoadInternal+57^%IPM.Main.1^1d^Load+8^%IPM.Main.1^1"d^ShellInternal+53^%IPM.Main.1^1d^Shell+5^%IPM.Main.1^1d^ZPM+2^%ZLANGC00^1d^^^0
Committed: Uncommitted
CommandString: load /home/irisowner/zpm/tests/integration_tests/Test/PM/Integration/_data/malformed-module
Source: n/a

The details view prints the raw %Status, not the formatted error text, so the ObjectScript stack is visible. That stack is what tells you which phase of the pipeline the failure came from. In the example above, the parse error surfaced inside GetModuleObjectFromStream, called from LoadNewModule, so the manifest was rejected before any resource was touched.

Add -phases to see how long each lifecycle phase took and which one failed:

zpm:USER>history details 13 -phases

ID: 13
Action: install
Package: isc.rest
Version: v1.2.2
UserName: irisowner
Time: Started At 2026-04-06 15:20:11; Ended At 2026-04-06 15:20:23
Success: 1
Committed: Committed
CommandString: install isc.rest
Source: registry (registry): https://pm.community.intersystems.com

Phases:
Name Time Success

Initialize 2026-04-06 15:20:15 (0 sec) Success
Reload 2026-04-06 15:20:15 (2 sec) Success
Validate 2026-04-06 15:20:17 (0 sec) Success
Compile 2026-04-06 15:20:17 (5 sec) Success
Activate 2026-04-06 15:20:22 (0 sec) Success

A phase with no elapsed time never finished, which localizes a hang or a crash to that phase. Phase timings are also the fastest way to see where a slow install is spending its time.

Looking across namespaces

The log is per-namespace, but -globally walks every namespace on the instance and prints the records grouped by namespace. Namespaces without IPM contribute no rows. Note that -limit applies per namespace, not to the combined output.

history find -globally -Daction=install -Dpackage=zpip

Record IDs are only unique within a namespace, so an ID from a -globally listing is only meaningful in the namespace it was listed under. history details always reads from the current namespace.

Retention and purging

Availability: IPM v0.10.7+

The log grows without bound by default. Set HistoryRetain to a number of days to have IPM delete records older than that:

config set HistoryRetain 30
config get HistoryRetain
config set HistoryRetain 0

The purge runs on any IPM invocation, at most once per calendar day, and applies to every namespace rather than just the current one. It compares against TimeStart. 0 (the default) disables it.

To delete records on demand, use history delete with a filter. Deleting everything in the namespace requires -confirm, since there is no filter to constrain it:

history delete -Dpackage=my-module
history delete -confirm
history delete -globally -DTimeStart="<2026-01-01 00:00:00"

-globally bypasses the -confirm requirement, so history delete -globally with no filter deletes every record on the instance without prompting. Pass -verbose to see the count deleted from each namespace, and which namespaces were skipped for lack of permission.

Once a record is finalized, a trigger on the table rejects UPDATE statements against it, so the log is append-and-delete only.

Querying the table directly

%IPM_General.History is a normal SQL table, which is useful when you want aggregates, joins, or output going somewhere other than the terminal:

SELECT Package, COUNT(*) AS Failures
FROM %IPM_General.History
WHERE Action = 'install' AND Committed = 0
GROUP BY Package
ORDER BY Failures DESC

There are also two class queries for programmatic access, %IPM.General.History:Records for the current namespace and %IPM.General.History:GlobalRecords for all of them. Both take an ascend and a limit argument.

Common recipes

An install left the namespace in a bad state and you want to find failures whose changes were kept:

history find -Dcommitted=1 -DSuccess="0*"

Find out who installed a module and where it came from:

history find -Daction=install -Dpackage=my-module

Then run history details <id> on the result to see the full repository URL.

Find actions that never finished, which usually means a killed process or a hang. In-progress records have their status left at exactly 0, so an exact match finds them without also matching error statuses:

history find -DSuccess=0

Reconstruct what a CI job did, in the order it happened:

history find -sort asc -limit 0 -DUserName=cibuild
⚠️ **GitHub.com Fallback** ⚠️