Resolving clearNamedQueryCache Silent Failure in Ignition

Claire Rousseau8 min read
B&R AutomationHMI / SCADATroubleshooting
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

Symptom: Silent Return on an Invalid Path

Calling system.db.clearNamedQueryCache with a path that does not match any named query produces no error. The call completes and returns None, and execution continues past it. The function reference says a GatewayException is thrown for a malformed path, but the implementation does not throw one.

The behavior is easy to reproduce from a Perspective component event script, which runs in gateway scope:

def runAction(self, event):
    system.perspective.print("start test")
    from java.lang import Throwable
    try:
        ret = system.db.clearNamedQueryCache(path = "blablabla")
        system.perspective.print("ret: %s" % (repr(ret)))
    except Throwable as t:
        system.perspective.print("caught Throwable: %s" % (repr(t)))
    except Exception as e:
        system.perspective.print("caught Exception: %s" % (repr(e)))
    system.perspective.print("end test")
    self.props.value = 0

The expected output is caught Throwable. The actual output is start test, ret: None, end test. Neither the Java Throwable handler nor the Python Exception handler fires.

Mechanism: Void All the Way Down

The scripting function wraps internal Java methods that are declared void at every layer. Nothing below the script call reports whether a cache entry existed, whether one was evicted, or whether the path resolved to a query at all. The scripting layer therefore has nothing to return except None, and it does not validate the path before handing it down.

Three practical consequences follow:

  • Success and failure look identical. A valid path with a populated cache, a valid path with caching disabled, and a typo all return None with no exception.
  • try/except around the call is dead code. No exception handler around this function will ever run for a bad path, so any error UI or alarm built on that handler never fires.
  • The failure surfaces later as stale data. A cache clear that silently misses leaves the old result set in place until the query's configured cache duration expires. Operators see stale values; the script log shows nothing.

The documentation and the implementation disagree. Treat the implementation as the contract: build validation outside the function rather than waiting for an exception that is not coming.

Symptoms Versus Causes

What you see Likely cause What decides it
Call returns None, no exception, data still stale Path string does not match the named query (typo, wrong folder, old name after a rename) Compare the string character-for-character with the path in the Designer Named Queries tree
Call returns None, data refreshes only after a delay Clear missed; cache expired on its own timer Time the refresh against the query's configured cache duration
Call returns None, data was never stale Caching not enabled on that named query, so there was nothing to clear Check the query's caching settings in the Designer
Exception handler never fires on garbage input Function does not validate paths; documented GatewayException is not raised Run the reproduction above with a nonsense path
Switching path= keyword to a positional argument changes nothing Argument style is not the cause; behavior is identical either way Run both forms; output matches

Approach Comparison

Several ways exist to get feedback out of a function that gives none. They differ in whether they work at all, what they cost at runtime, and what they catch.

Approach Catches bad paths? Runtime cost Side effects Verdict
Wrap the call in try/except (Throwable or Exception) No None False confidence Non-functional; remove or replace
Change argument style (positional instead of keyword) No None None No effect on this function
Pre-validate against a maintained registry of cached query paths Yes, deterministically Set lookup Registry must be kept in sync with the project Recommended for runtime
Pre-validate by executing the query (system.db.runNamedQuery) first Yes, via the run's own error Full database round trip Executes the query; unsafe for update/insert queries; repopulates cache right before clearing it Avoid at runtime
Stale-data functional test Yes, and proves the clear actually took effect Manual, one-time Requires writing test data Required at commissioning

Use two layers: a registry-checked wrapper at runtime, and a stale-data test at commissioning and after any named query rename or move. The wrapper stops typos from ever reaching the silent function; the test proves the path that passes the wrapper actually evicts the cache.

Recommended Guard: Registry-Checked Wrapper

Before anything else, confirm which named queries in the project have caching enabled. Only those belong in the registry; clearing a non-cached query is a no-op and signals a design mistake if it appears in code.

  1. Inventory cached queries. Open each named query in the Designer and check its caching settings. Record the exact path as shown in the Named Queries tree (folder and name). Do not move on until every cached query is listed.
  2. Create a project library script holding the registry and the wrapper. The script name and example path below are placeholders; replace them with your own.
    
    

    Confirm: the script saves without syntax errors and the gateway logger NQCache appears once the function is first called.

  3. Replace every direct call to system.db.clearNamedQueryCache in views, event scripts, and gateway scripts with nqcache.clear(...). A project-wide search for the function name finds them. Confirm: the search returns hits only inside the library script.
  4. Handle the wrapper's exceptions where the call originates. In a Perspective event:
    def runAction(self, event):
        try:
            cleared = nqcache.clear("Folder/QueryName")
            system.perspective.print("cache clear requested: %s" % cleared)
        except (ValueError, TypeError) as e:
            system.perspective.print("rejected: %s" % e)

    Confirm: passing "blablabla" now prints rejected: Unknown or uncached named query path: 'blablabla' instead of silently returning.

  5. Log wording matters. The log line says "requested", not "cleared". The underlying function cannot confirm eviction, so the log must not claim it.

Pitfalls: Renames, Scope, and Argument Style

  • Renaming or moving a named query breaks clears silently. Every call pointing at the old path keeps returning None while the new query's cache is never touched. With the wrapper in place, the old path is still in the registry and passes the check, so update the registry in the same change as the rename, then rerun the stale-data test.
  • Argument style is not the fault. Many older system.* functions handle keyword arguments poorly, so trying the positional form is a reasonable first check. For this function, clearNamedQueryCache("blablabla") and clearNamedQueryCache(path = "blablabla") behave identically. Pick one style and keep it consistent inside the wrapper.
  • Scope determines which project's cache is targeted. Perspective scripts run in gateway scope with an implied project. Gateway event scripts and scripts called from outside a project context may use a different signature; read the scope-specific syntax in the function reference for your Ignition version before reusing the wrapper there. A clear aimed at the wrong project fails exactly as silently as a typo.
  • Catching java.lang.Throwable does not help. It is the right pattern for surfacing Java exceptions that Python's Exception misses, but here no exception exists at either layer. Keep the pattern for other system.db calls; do not rely on it for this one.
  • Clearing a non-cached query proves nothing. If caching is off, the query always hits the database and the clear has no observable effect. The registry excludes these queries so a clear call against one is rejected rather than mistaken for success.

Commissioning Verification: Stale-Data Test

The wrapper proves a path is in the registry. Only a functional test proves the path evicts the cache. Run this once per cached query at commissioning and again after any rename, move, or project copy.

  1. Confirm caching is active on the target query in the Designer and note its cache duration. The test must finish well inside that window, or natural expiry will mask a failed clear.
  2. Run the query with a fixed parameter set using system.db.runNamedQuery from the Script Console or a test view. Record the returned value for one specific row.
  3. Change that row directly in the database using a database tool or a test table, not through the named query.
  4. Rerun the query with the identical parameter set. Expect the old value. If the new value appears, caching is not active for that parameter set; fix the query settings before continuing.
  5. Call nqcache.clear() with the query's path. Confirm the NQCache log entry appears in the gateway logs.
  6. Rerun the query with the identical parameter set. Expect the new value. If the old value persists, the registry path does not match the query or the call targeted the wrong project; correct the path and repeat from step 3.
  7. Run the negative test. Call the wrapper with a nonsense path and confirm it raises ValueError. Then call the raw system.db.clearNamedQueryCache with the same nonsense path and confirm it still returns None without raising. If the raw call now raises, the platform behavior has changed after an upgrade; review every handler around cache clears before signing off.

FAQ

What happens if I pass a misspelled path to system.db.clearNamedQueryCache?

The call returns None and raises nothing, despite the documented GatewayException. The real query's cache stays populated and clients receive stale data until the configured cache duration expires.

What happens if I call clearNamedQueryCache with a positional argument instead of path=?

Nothing changes. Both clearNamedQueryCache("x") and clearNamedQueryCache(path = "x") return None silently for an invalid path, so argument style is not the fix.

What happens if I wrap clearNamedQueryCache in try/except java.lang.Throwable?

The handler never runs for a bad path because the function and its internal Java methods are void and perform no path validation. Replace the try/except with a pre-check against a registry of known cached query paths.

What happens if I clear the cache on a named query that has caching disabled?

The call returns None, identical to a successful clear, and has no effect because every execution already hits the database. Keep such queries out of your clear registry so these calls are rejected instead of looking like success.

How do I confirm clearNamedQueryCache actually cleared the cache?

Run a stale-data test: execute the query, change the underlying row directly in the database, confirm the rerun returns the old value, call the clear, and confirm the next rerun returns the new value. Finish with a negative test showing a bogus path is rejected by your wrapper.

Back to blog