Ignition Perspective reports Error_InvalidPathSyntax because the Accordion contains two competing binding layers: the complete props.items array comes from view.params.items, while the third item's header has its own indirect tag binding. That indexed binding references a popup parameter that the caller does not supply.
Popup parameter contract
The term parameter contract means the set of view parameters that the caller supplies and the popup consumes. The popup declares view.params.id and view.params.items as input parameters. The double-click event passes both values:
params={'items': items, 'id': popup_id}
Each constructed item already contains its header text at header.content.text. It also passes the UDT path to the embedded body view as body.viewParams.parent_path. The popup therefore has all data required to display the header without another tag lookup.
The conflicting indirect binding references view.params.udt_path, but that parameter is neither declared in the popup JSON nor passed by openPopup. The popup cannot substitute reference {1} into the configured tag path.
-
Check 1: Inspect the popup's input parameters. Expect
idanditems. -
Check 2: Inspect one received item. Expect populated values at
header.content.textandbody.viewParams.parent_path.
Accordion binding ownership
A property binding owns the property it targets. The Accordion has a property binding from view.params.items to the complete props.items array. It also has a tag binding directly on props.items[2].header.content.text with this configuration:
reference {1}: {view.params.udt_path}
tag path: {1}/tagname_1
mode: indirect
fallback delay: 2.5
The term indirect tag binding means that Perspective builds a tag path by substituting one or more reference values into a path template. Here, reference {1} produces no value, so Perspective cannot build the tag path and reports:
Error_InvalidPathSyntax root/Accordion.items[2].header.content.text
Unable to build tag path, indirection reference(s) [1] did not produce a value.
The indexed binding explains why the fault names only the third item: array index [2] is the third position. Logging the array before opening the popup can show valid header text because the source array is valid. After the array reaches the Accordion, the more specific binding attempts to replace that third header with a tag value and fails.
| Observation | Cause | Expected diagnostic |
|---|---|---|
| First and second headers display | No indexed header binding targets them | Valid values at indexes [0] and [1]
|
| Third header faults | Binding targets items[2]
|
Error path includes items[2].header.content.text
|
| Logged array contains the text | The failure occurs in the destination binding layer | Source value remains populated |
| Fault appears only with some array states | The indexed binding becomes relevant when index [2] exists |
No third-item path exists while the array has fewer than three entries |
Check 3: Inspect the Accordion's property configuration. Expect both the top-level props.items property binding and the unwanted indexed binding before correction.
Indexed binding removal
Use one owner for dynamic Accordion content. Because makeItem already places the alarm tag name in the header, retain the top-level property binding and remove the tag binding from props.items[2].header.content.text.
- Select the Accordion in the popup view.
- Locate
props.items[2].header.content.textin the component property configuration. - Remove its indirect tag binding, including reference
{1}and tag-path template{1}/tagname_1. - Retain the property binding from
props.itemstoview.params.items. - Save the view and reopen the popup so the session receives the revised component definition.
Passing udt_path would satisfy the missing reference, but it would leave a fixed binding attached to array position three. Sorting by severity can move a different alarm into that position, so the binding could display a header unrelated to the item body. Fixed-index bindings are the wrong mechanism for arrays whose membership or order changes.
Check 4: Reinspect the component configuration. Expect one binding on props.items and no binding on props.items[2].header.content.text.
Stable alarm-item construction
Build each Accordion item as a complete value before assigning the array. The existing makeItem structure follows that pattern: it sets severity, expanded, the text header, and the body view parameters in one operation. Sorting then creates the display order from severity.
The removal function should return a new list instead of modifying the supplied collection with pop. In-place mutation can make change detection and concurrent alarm updates harder to reason about. A non-mutating implementation is:
def removeItem(items, alarm_type, tagname):
return [item for item in items
if item['header']['content']['text'] != tagname]
The alarm_type argument is not used by this implementation and may be removed from both the function signature and call after checking for other callers. The matching key remains header.content.text, so duplicate tag names would remove every matching entry. If duplicate names are valid, store and compare a unique alarm identifier supplied by the application rather than relying on visible text.
Rapid changes from separate alarm properties can also produce competing read-modify-write operations: each script reads self.custom.items, changes its copy, sorts it, and writes the result. If alarms can transition together, verify that no update disappears. Centralizing array reconstruction from current alarm states avoids last-write-wins behavior.
Check 5: Activate and clear one alarm repeatedly. Expect one item while active, no matching item after clearing, and descending order by severity.
Popup invocation and data transfer
The double-click event opens alarm_accordion only when self.custom.in_alarm is true. It passes the item array, popup identifier, and pointer position. Keep the item dictionary shapes identical across every alarm so the Accordion does not receive partially populated entries.
items = self.custom.items
system.perspective.openPopup(
id=popup_id,
view='alarm_accordion',
position={'top': event.pageY, 'left': event.pageX},
params={'items': items, 'id': popup_id},
showCloseIcon=False,
draggable=True
)
The popup's close action uses view.params.id, so the passed id must continue to match the popup identifier. The header needs no udt_path parameter after removal of the indexed binding; the embedded alarm_popup body receives its path through each item's parent_path.
Check 6: Open the popup from an alarming component. Expect its position to follow event.pageX and event.pageY, every header to match the passed array, and the close action to address the same popup_id.
End-to-end commissioning verification
-
Check 7: Open the popup with one active alarm. Expect one Accordion item with the text supplied by
prop['tagname']. -
Check 8: Open it with three or more active alarms. Expect a valid third header and no
Error_InvalidPathSyntaxatitems[2]. -
Check 9: Change alarm severities or activate alarms in a different order. Expect descending
severityorder while each header remains paired with its ownalarm_nameandparent_path. - Check 10: Clear an alarm and reopen the popup. Expect the matching item to be absent without disturbing the remaining items.
-
Check 11: Exercise the embedded body view for every row. Expect each
alarm_popupinstance to receive the correspondingalarm_nameandparent_path.
FAQ
How do I fix Error_InvalidPathSyntax on Accordion items[2]?
Remove the indirect tag binding from props.items[2].header.content.text. Keep the complete props.items array bound to view.params.items.
How do I pass dynamic headers into a Perspective Accordion?
Populate header.content.text in each item dictionary before opening the popup, then pass the completed array through params={'items': items}.
How do I prevent sorted Accordion items from showing the wrong header?
Avoid bindings tied to fixed indexes such as items[2]. Store the header and body parameters inside the same item, then sort complete items by severity.
How do I verify the Perspective popup fix?
Open the popup with at least three alarms, reorder them by changing activation order or severity, and clear one alarm. Expect every header to remain paired with its body and no Error_InvalidPathSyntax message.