Problem Overview
In Ignition Designer, a valueChanged event script on a component's custom property fails to write the updated value to a Memory tag. The two most common root causes are (1) using a string literal instead of currentValue.value to reference the incoming value, and (2) a Python indentation or line-continuation error that silently prevents the script from executing. A third cause — the tag path being marked read-only at the Tag Provider level — produces no script error but leaves the tag unchanged.
valueChanged event signature and system.tag.writeAsync API are consistent across both generations, so all three root causes apply to any current release.Root Causes
| # | Symptom | Root Cause |
|---|---|---|
| 1 | Tag receives the literal string "self.custom.Value" instead of True/False
|
Value wrapped in quotes — treated as a string literal, not a variable dereference |
| 2 | Script appears to fire but tag never changes; no error in logs | Two statements concatenated on one line without a newline; Python parses them as a single expression and the write call is never reached |
| 3 | Script runs without exception, writeAsync returns, tag stays unchanged |
Tag path is inside a read-only folder in the Tag Browser — write is silently discarded by the gateway |
Corrected Script
The canonical valueChanged handler for pushing a custom property value to a Memory tag:
def valueChanged(self, previousValue, currentValue, origin, missedEvents):
tagPath = "[default]Path/To/Your/MemoryTag"
value = currentValue.value # unwrap QualifiedValue → raw Python value
system.tag.writeAsync([tagPath], [value])
Key corrections versus the broken version:
-
Remove quotes from the value assignment.
value = "self.custom.Value"is a string literal.value = currentValue.valueunpacks theQualifiedValueobject that Ignition passes into everyvalueChangedhandler. -
Each statement on its own line. Python is whitespace-sensitive. If
tagPath = "..."andvalue = ...appear on the same physical line without a semicolon separator, the interpreter raises aSyntaxErroror silently misparses the block. Use four separate lines as shown above. -
Consistent 4-space indentation. Mixing tabs and spaces inside the function body causes an
IndentationErrorat runtime in Jython (Ignition's scripting engine).
Diagnosing Whether the Script Fires at All
Before fixing the write logic, confirm the event handler is being invoked:
- Add a
system.perspective.print()(Perspective) orprint(Vision) as the first line of the handler. - Open Tools → Script Console or check the Designer console output tab. If nothing prints when the custom property changes, the binding that drives the property is not firing — debug the binding first.
- Wrap the write in a try/except to surface silent failures:
def valueChanged(self, previousValue, currentValue, origin, missedEvents): tagPath = "[default]Path/To/Your/MemoryTag" value = currentValue.value try: system.tag.writeAsync([tagPath], [value]) except Exception as e: system.perspective.print("Tag write failed: " + str(e)) - In the Gateway, navigate to Status → Diagnostics → Logs and filter on
Tagsto catch write-rejected messages.
Fixing Read-Only Tag Permissions
If the script fires and no exception is thrown but the tag value never changes, check the tag's security settings:
- In Designer's Tag Browser, right-click the target Memory tag → Edit Tag.
- On the Security tab, verify Read Only is not checked and that the active security zone grants Write access to the gateway user running scripts.
- If the tag lives inside a folder, right-click the folder → Edit Folder — folder-level read-only overrides individual tag settings and is easy to miss.
- After clearing the restriction, test with
system.tag.write()in the Script Console first to confirm write access before re-testing the event handler:system.tag.write("[default]Path/To/Your/MemoryTag", True)
Parameter Reference: system.tag.writeAsync
| Parameter | Type | Notes |
|---|---|---|
tagPaths |
List[str] |
Full tag path including provider prefix, e.g. [default]Folder/TagName
|
values |
List[any] |
Must be same length as tagPaths; Ignition coerces to the tag's data type |
callback |
callable (optional) |
Invoked with a List[QualityCode] when the write completes; omit for fire-and-forget |
| Return | None |
Does not block; use the callback or system.tag.write() if you need synchronous confirmation |
For boolean Memory tags, pass Python True/False directly. Ignition's type coercion maps them to the tag's Boolean data type without explicit casting.
system.tag.write() (synchronous) is acceptable in the Script Console for testing but avoid it in valueChanged handlers — blocking the event thread can cause UI lag in Vision and transaction timeouts in Perspective.Why does currentValue need .value in a valueChanged script?
Ignition passes a QualifiedValue object into valueChanged, not a raw Python scalar. Access the underlying value with currentValue.value; use currentValue.quality to check whether the source data is good before writing.
How do I confirm system.tag.writeAsync actually wrote the value?
Supply a callback function as the third argument: system.tag.writeAsync([path], [val], lambda results: system.perspective.print(str(results))). The callback receives a list of QualityCode objects; QualityCode.Good confirms success.
The valueChanged script runs in the Script Console but not in the live project — why?
Script Console runs as the Designer user with full privileges. In the live project the gateway service user may lack Write access to the tag or the tag folder. Check Tag Browser → Edit Tag → Security and confirm the Read Only flag is cleared at both the tag and folder levels.
Can I use system.tag.writeAsync inside a Perspective component's valueChanged event?
Yes. In Perspective, valueChanged runs server-side (gateway scope), so system.tag.writeAsync is fully available. Do not use Vision-only functions like system.gui.* in the same handler.
What causes "two statements on one line" to silently fail rather than throw a SyntaxError?
When tagPath = "..."value = currentValue.value appears without whitespace, Jython parses "..."value as implicit string concatenation, not a second assignment, so no SyntaxError is raised — the subsequent writeAsync call simply never executes. Always keep each statement on its own line.