Configuring Custom Icon Libraries in Ignition 8.3 Perspective

Daniel Price13 min read
HMI / SCADAOther ManufacturerTechnical Reference
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Ignition 8.3 no longer reads Perspective icon libraries from data/modules/com.inductiveautomation.perspective/icons/<repository name>.svg. The 8.1 documentation still points there, but on an 8.3 gateway a search for material.svg returns nothing. A custom library is now a gateway configuration resource. It lives under data/config/resources/core/com.inductiveautomation.perspective/icons/<library name>/ and contains three files: the SVG sprite sheet, a config.json, and a resource.json. The gateway only loads the library after it rescans its configuration. This layout has been confirmed working on 8.3.0, 8.3.1 and 8.3.6, and the final 8.3 release handles icons the same way as the pre-release builds.

Which hops does a Perspective icon request cross in 8.3?

An Icon component in a Perspective session or the Designer icon browser asks for an icon by <library>/<icon id>. The request goes over HTTP to the gateway. There the Perspective icon manager answers it from a parsed, cached copy of the library. The cache is not built when the request arrives. It is built when the resource collection manager reports that a resource was added or changed.

The gateway stack trace for a failed library shows the call chain:

  1. A file tree change on disk (ResourceCollectionManagerImpl.onFileTreeChange).
  2. A collection update (ResourceCollectionLifecycleFactory).
  3. The resource lifecycle hook (NamedResourceHandler$Lifecycle.onAfterChanges).
  4. The icon manager (IconManagerImpl.onResourceAdded or onResourcesUpdated).
  5. An XML parse in IconManagerImpl.computeLibraryAndCache.

If any hop fails, the library never reaches the cache. The browser then gets a 404, and the Designer shows nothing for that library.

Hop What it does Failure symptom Where to look
Filesystem Holds the library folder under the core collection Library never appears Folder path, folder name, file names and extensions
Collection scan Detects new or changed resources Edits are ignored until a restart or rescan Gateway restart, scan endpoint, or Scan File System button
Resource descriptor resource.json lists the files that belong to the resource Library missing or incomplete The files array against the actual file names
Icon manager parse Parses the SVG as XML and caches the icons Unable to parse <name> icon library collection=core in the log Gateway log, P.IconManager logger
HTTP delivery Serves icons to the browser or Designer Failed to load resource: the server responded with a status of 404 (Not Found) Browser DevTools console and network tab

Read this table from the top down. A 404 at the last hop almost never means the HTTP layer is at fault. It means an earlier hop never produced a cached library.

Why does the 8.1 icons folder stop working after the upgrade?

In 8.1, each library was a single SVG file at a fixed path:

  • Windows: C:\Program Files\Inductive Automation\Ignition\data\modules\com.inductiveautomation.perspective\icons\<repository name>.svg
  • Linux and macOS: the same relative path under the install directory.

In 8.3, gateway configuration moved into resource collections under data/config/resources/. Icon libraries moved with it. The icon manager now takes its input from the core collection, not from the module data folder.

An in-place upgrade from 8.1 moves the old SVG files into a hidden, timestamped folder, for example /usr/local/bin/ignition/data/modules/com.inductiveautomation.perspective/.migrated-icons-20260526-0932. On that upgraded gateway the icons keep working. Dropping a new SVG into the old icons/ folder on an 8.3 gateway does nothing, because nothing in 8.3 reads that folder. Also, a .migrated-icons-* folder does not appear on a gateway that was installed directly as 8.3. An empty or missing migrated-icons folder after a restart is normal on such a gateway and is not a diagnostic signal.

Which placement and reload options are available, and which one should you use?

Two decisions matter: where the library lives, and how the gateway is told to reload it.

Option Works on 8.3 Downtime Survives backup/restore to a fresh 8.3 gateway Notes
SVG in data/modules/com.inductiveautomation.perspective/icons/ (8.1 layout) No, for new libraries n/a No Only upgrade migration uses this location
Migrated icons in .migrated-icons-<timestamp> Yes, on the upgraded gateway only None No. They were missing after restoring onto a directly installed 8.3 gateway Treat as a one-time upgrade artifact
Resource folder under data/config/resources/core/com.inductiveautomation.perspective/icons/<library>/ Yes (8.3.0, 8.3.1, 8.3.6) Depends on reload method Verify with a test restore (see below) Supported 8.3 layout
Reload method How to trigger it Downtime Best use
Gateway restart Service restart Full outage Commissioning, or when other changes also need a restart
Config scan endpoint POST /data/api/v1/scan/config None Scripted deployment and CI pipelines
Scan File System button Top right of the gateway Platform Overview page None Manual changes on a running production gateway

Recommendation: Build every custom library, including libraries carried over from 8.1, as a resource folder under the core collection. Reload with Scan File System for manual work, or with the scan endpoint for scripted deployments. Restart only when the gateway is already scheduled to go down.

What goes inside the library folder?

The folder name is the library name. That is the first half of every icon path the Designer writes into views. The SVG file can have any name. Standardizing on one name, such as icons.svg, means the same config.json and resource.json can be copied into every library without editing.

<install dir>/data/config/resources/core/
  com.inductiveautomation.perspective/
    icons/
      <library name>/
        icons.svg
        config.json
        resource.json

config.json contains one entry, svgFileName. Its value must match the SVG file name exactly:

{ "svgFileName": "icons.svg" }

resource.json is the resource descriptor. Its files array must list both config.json and the SVG:

{
  "scope": "A",
  "version": 1,
  "restricted": false,
  "overridable": true,
  "files": [
    "config.json",
    "icons.svg"
  ],
  "attributes": { }
}
Field Value Purpose
scope "A" Resource scope. Use the value shown.
version 1 Resource version
restricted false Resource is not restricted
overridable true Collections that inherit from core can override the resource
files ["config.json", "<svg name>"] Files that belong to the resource. Names must match the files on disk.
attributes { } Empty for icon libraries

During pre-release there was a proposal to drop the config.json indirection and have resource.json point straight at the SVG. The final release kept the same icon mechanism, so build libraries with config.json included.

The SVG itself is a sprite sheet. Each icon is an element inside it whose id becomes the icon name. Format it the way the "Images and Icons in Perspective" section of the Ignition User Manual describes. Existing 8.1 libraries, such as the Bootstrap set from the Ignition Exchange, already use this format and can be reused unchanged.

How do I build the library on a running 8.3 gateway?

This procedure assumes the gateway has already started at least once, so the core resource collection exists. For a gateway that has never started, see the next section first.

  1. Go to <install dir>/data/config/resources/core/. On Windows the default install directory is C:\Program Files\Inductive Automation\Ignition.
  2. Create com.inductiveautomation.perspective/ if it does not exist. Inside it, create icons/.
  3. Inside icons/, create a folder named exactly after the library, for example bootstrap-icons/. Use the same name the library had on the source gateway so existing icon paths still resolve.
  4. Copy the SVG sprite sheet into the library folder. Check that it really is a .svg file with that extension. Windows Explorer hides known extensions, so a file that looks like bootstrap-icons may actually be named bootstrap-icons.svg.txt, or may be a folder.
  5. Create config.json with svgFileName set to the exact SVG file name.
  6. Create resource.json with a files array that lists config.json and the exact SVG file name.
  7. Make sure the gateway service account can read the new folders. On Linux, match the owner and permissions of the neighboring folders in core.
  8. Trigger a rescan. Either click Scan File System on the gateway Platform Overview page, or send POST /data/api/v1/scan/config to the gateway, or restart the gateway.
  9. Open the gateway log and filter for the icon manager before opening the Designer.

For a scripted deployment, the scan call is a single POST:

curl -X POST "http://<gateway-host>:<port>/data/api/v1/scan/config"

Add authentication to that call according to the API access configured on your gateway. An unauthenticated or rejected call leaves the new resource unloaded, with no error in the icon manager log.

What breaks when the folders go onto a gateway that has never started?

The procedure above assumes the core collection already exists. On a clean install that has never run, it does not. When a gateway starts, it looks for data/config/resources/core/config-mode.json:

  • If the file is present, the gateway loads the existing collection.
  • If the file is missing and the folder is empty, the gateway creates the collection from scratch.
  • If the file is missing and the folder is not empty, which is what happens after pre-creating the icon folders, startup fails.

A failed startup shows in wrapper.log as:

java.nio.file.FileAlreadyExistsException: Resource collection path '/usr/local/bin/ignition/data/config/resources/core' exists but is not empty

The gateway reports that it is unable to create the core resource collection. Deleting the icon folders afterwards did not bring the gateway back in one reported case. That gateway had to be reinstalled and restored from backup. Prevent the problem instead of recovering from it.

If you stage icon libraries into an install before its first start, also create data/config/resources/core/config-mode.json:

{
  "title": "Core",
  "description": "Core collection of locally managed Gateway configuration resources",
  "enabled": true,
  "inheritable": true,
  "parent": "external"
}
Gateway state config-mode.json present? Action
Has started at least once Yes, created by the gateway Add the icon folders only, then rescan
Fresh install, never started No Either start the gateway once first, or create config-mode.json along with the icon folders
Fails with FileAlreadyExistsException on core No Add config-mode.json with the content above and restart. If the gateway still will not start, reinstall and restore a gateway backup.

The simplest rule: start a new 8.3 gateway once and let it finish commissioning before you place any files under data/config/resources/core/.

What does the gateway log show when the SVG will not parse?

A 404 in the browser console with correct config.json and resource.json files almost always means the SVG failed to parse. In one case a sprite sheet that worked on an 8.3.0 gateway returned 404 on a new 8.3.1 gateway. The file had been corrupted slightly while it was copied between machines. The icon mechanism had not changed between releases, and the gateway log showed the parse failure.

The icon manager logs parse failures under the P.IconManager logger:

E [P.IconManager] Unable to parse PlantPAxIcons icon library collection=core
org.xml.sax.SAXParseException: An invalid XML character (Unicode: 0x0) was found in the element content of the document.
  at ...IconManagerImpl.computeLibraryAndCache(IconManagerImpl.java:182)
  at ...IconManagerImpl.onResourceAdded(IconManagerImpl.java:150)

The same error appears twice: once from onResourceAdded and once from onResourcesUpdated. Both mean the same thing: the library was found, but it is not valid XML. The icon manager is strict. One bad byte rejects the whole library, not just one icon.

Log or console evidence Cause Fix
Other SAXParseException text Malformed markup: unclosed tags, a stray character, or a broken XML declaration Run the SVG through an XML validator. Fix the line and column the exception reports.
No icon manager entry, and the library is missing Resource not detected: wrong path, wrong folder level, mismatched files entry, or no rescan Recheck the folder tree and file names, then rescan
Browser 404 with a clean log Icon path references a library or icon id that does not exist Compare the path in the view with the library folder name and the id values in the SVG

Read the log through the gateway web interface log viewer, or in the log files under the install directory. Check it after every rescan, before you start troubleshooting in the Designer.

Why do icon paths break after moving a project between gateways?

Views do not store the icon graphic. They store a path such as <library>/<icon id>. The library segment comes from the library folder name under icons/, not from the SVG file name. If a project that used bootstrap-icons/... on 8.1 lands on an 8.3 gateway where the folder is named differently, every icon reference misses and components render blank.

Item Must match Can be arbitrary
Library folder name The library name used in every icon path in the project No
SVG file name svgFileName in config.json and the entry in resource.json files Yes. For example, icons.svg in every library.
Icon id in the sprite sheet The icon segment of the path No

Before you import a project, list the library names it references. Create folders with exactly those names on the target gateway. Using one SVG file name for every library means only the folder name changes per library, which removes a common copy-paste mistake. The trade-off is that several open sprite sheets in an editor all show the same file name.

Why are migrated 8.1 icons missing after a backup restore?

An 8.1-to-8.3 upgrade keeps old libraries working from data/modules/com.inductiveautomation.perspective/.migrated-icons-<timestamp>/. When a backup of that upgraded gateway was restored onto a gateway installed directly as 8.3, the icons were missing from both the old path and the new one. Do not rely on the migrated folder to carry icons between gateways.

To make the libraries portable, convert each migrated library into a proper resource:

  1. Find each SVG in the .migrated-icons-<timestamp> folder on the upgraded gateway. On Linux the folder is hidden, so use ls -a.
  2. For each SVG, create data/config/resources/core/com.inductiveautomation.perspective/icons/<library name>/. The library name is the original SVG file name without .svg, because that is what the existing icon paths use.
  3. Copy the SVG into the new folder. Add config.json and resource.json as shown above.
  4. Rescan, then check the log for P.IconManager errors.
  5. Take a new gateway backup. Restore it to a test gateway that was installed directly as 8.3. Confirm the library folders exist under data/config/resources/core/ and that the icons render.

If the test restore does not bring the icons over, add the library folders to your deployment procedure as a separate step, using the scan endpoint after the files are copied. Open a case with Inductive Automation support about backup coverage.

How do I confirm the library is served end to end?

Check each hop in the same order the request travels:

  1. Filesystem. List the library folder. It should contain exactly the SVG, config.json and resource.json, with names that match the files array character for character, including case on Linux.
  2. JSON validity. Parse both JSON files with any JSON validator. A trailing comma or smart quotes pasted from a document breaks the descriptor.
  3. XML validity. Open the SVG in an XML-aware editor or validator and confirm there are no null bytes. The file should start with the <svg root element, or an XML declaration followed by it.
  4. Rescan. Click Scan File System or POST to /data/api/v1/scan/config.
  5. Gateway log. Confirm there is no Unable to parse <name> icon library collection=core entry from P.IconManager timestamped after the rescan.
  6. Designer. Open the icon selector on an Icon component. The library should appear under its folder name, with every icon id from the sprite sheet listed.
  7. Browser session. Open a Perspective session that uses an icon from the library, with DevTools open. Confirm the network tab shows no 404 for the icon request and the console shows no Failed to load resource error.

FAQ

What happens if I put a custom SVG in the old icons folder on Ignition 8.3?

Nothing loads. Ignition 8.3 reads icon libraries only from data/config/resources/core/com.inductiveautomation.perspective/icons/<library>/, together with config.json and resource.json. The old data/modules/.../icons/ path is used only by 8.1 upgrade migration.

What happens if the library folder name doesn't match the name used in my 8.1 project?

Every icon reference in the project misses and renders blank, because views store paths as <library>/<icon id> and the library segment is the folder name. Rename the folder to match. The SVG file name inside it does not matter.

What happens if I create the icon folders before an Ignition 8.3 gateway first starts?

Startup fails with FileAlreadyExistsException: Resource collection path '.../data/config/resources/core' exists but is not empty, because config-mode.json is missing. Add data/config/resources/core/config-mode.json with the Core collection definition, or let the gateway start once before adding any files.

Why does Ignition 8.3 return a 404 for my custom icons when the JSON files are correct?

The SVG almost certainly failed to parse, so the icon manager never cached the library. Look in the gateway log for P.IconManager entries such as SAXParseException: An invalid XML character (Unicode: 0x0)

What happens if I restore an upgraded gateway's backup onto a fresh 8.3 install?

Icons that live only in the hidden .migrated-icons-<timestamp> folder were not present after such a restore. Convert each migrated library into a resource folder under data/config/resources/core/ before taking the backup, then confirm with a test restore.

Back to blog