Client tag expression with hasRole NPE

I've got a weird issue, and I'm not sure if this is because of my usage or if there is an actual problem going on here.

I want to use a Vision Client tag to generate some bit based on the current user roles. IE if the user has the "Operator" role then we will turn on a client tag so that it can be easily tweaked down the road.

The expression that I'm using is:

hasRole('Operator',{[System]Client/User/Username})

The User Source I'm using has a failover to another user source.

The main user source is an Ignition internal, the failover is AD.

When I log in to the designer or project with a known user on the default (non-failover) user source, the expression works as expected.

However, if I log in with a user from the failover user source, I get the following NPE:

Error
ERROR com.inductiveautomation.factorypmi.application.sqltags.project.ExpressionTagBinding -- Error executing expression for tag "testing".
com.inductiveautomation.ignition.common.expressions.ExpressionException: Error retrieving user from gateway.
	at com.inductiveautomation.ignition.client.expressions.ClientFunctionFactory$HasRoleFunctionClient.getRoles(ClientFunctionFactory.java:302)
	at com.inductiveautomation.ignition.client.expressions.ClientFunctionFactory$HasRoleFunctionClient.execute(ClientFunctionFactory.java:272)
	at com.inductiveautomation.ignition.client.expressions.ClientDynamicDispatchFunction.execute(ClientDynamicDispatchFunction.java:43)
	at com.inductiveautomation.ignition.common.expressions.FunctionExpression.execute(FunctionExpression.java:69)
	at com.inductiveautomation.factorypmi.application.sqltags.project.ExpressionTagBinding.childInteractionUpdated(ExpressionTagBinding.java:44)
	at com.inductiveautomation.ignition.common.expressions.TagListener.tagChanged(TagListener.java:58)
	at com.inductiveautomation.factorypmi.application.sqltags.ProjectTagSubscriptionManager.subscribe(ProjectTagSubscriptionManager.java:119)
	at com.inductiveautomation.factorypmi.application.sqltags.system.AbstractClientSystemTagManager.subscribe(AbstractClientSystemTagManager.java:98)
	at com.inductiveautomation.factorypmi.application.sqltags.system.AbstractClientSystemTagManager.subscribe(AbstractClientSystemTagManager.java:106)
	at com.inductiveautomation.ignition.client.sqltags.impl.SubManagerAdapter.subscribeAsync(SubManagerAdapter.java:121)
	at com.inductiveautomation.ignition.client.sqltags.impl.SystemTagManager.lambda$subscribeAsync$16(SystemTagManager.java:310)
	at com.inductiveautomation.ignition.gateway.util.GroupMapCollate.lambda$groupMapIndexed$10(GroupMapCollate.java:89)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
	at java.base/java.util.HashMap$EntrySpliterator.forEachRemaining(Unknown Source)
	at java.base/java.util.stream.AbstractPipeline.copyInto(Unknown Source)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
	at java.base/java.util.stream.AbstractPipeline.evaluate(Unknown Source)
	at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(Unknown Source)
	at java.base/java.util.stream.ReferencePipeline.toArray(Unknown Source)
	at com.inductiveautomation.ignition.gateway.util.GroupMapCollate.groupMapIndexed(GroupMapCollate.java:98)
	at com.inductiveautomation.ignition.client.sqltags.impl.SystemTagManager.subscribeAsync(SystemTagManager.java:293)
	at com.inductiveautomation.ignition.client.tags.impl.ClientTagManagerImpl.lambda$subscribeAsync$9(ClientTagManagerImpl.java:300)
	at com.inductiveautomation.ignition.gateway.util.GroupMapCollate.lambda$groupMapIndexed$10(GroupMapCollate.java:89)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
	at java.base/java.util.HashMap$EntrySpliterator.forEachRemaining(Unknown Source)
	at java.base/java.util.stream.AbstractPipeline.copyInto(Unknown Source)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
	at java.base/java.util.stream.AbstractPipeline.evaluate(Unknown Source)
	at java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(Unknown Source)
	at java.base/java.util.stream.ReferencePipeline.toArray(Unknown Source)
	at com.inductiveautomation.ignition.gateway.util.GroupMapCollate.groupMapIndexed(GroupMapCollate.java:98)
	at com.inductiveautomation.ignition.client.tags.impl.ClientTagManagerImpl.subscribeAsync(ClientTagManagerImpl.java:288)
	at com.inductiveautomation.ignition.common.tags.model.TagManager.subscribeAsync(TagManager.java:79)
	at com.inductiveautomation.ignition.common.expressions.TagListener.startup(TagListener.java:70)
	at com.inductiveautomation.ignition.common.expressions.BoundTagExpression.startup(BoundTagExpression.java:109)
	at com.inductiveautomation.ignition.common.expressions.AbstractExpression.startup(AbstractExpression.java:61)
	at com.inductiveautomation.ignition.common.expressions.FunctionExpression.startup(FunctionExpression.java:62)
	at com.inductiveautomation.factorypmi.application.sqltags.project.ExpressionTagBinding.startBinding(ExpressionTagBinding.java:32)
	at com.inductiveautomation.factorypmi.application.sqltags.project.ProjectTag.startBindingImpl(ProjectTag.java:202)
	at com.inductiveautomation.factorypmi.application.sqltags.project.ProjectTag.startBinding(ProjectTag.java:189)
	at com.inductiveautomation.factorypmi.application.sqltags.ClientTagFolder.startBinding(ClientTagFolder.java:151)
	at com.inductiveautomation.factorypmi.application.sqltags.ClientTagFolder.startBinding(ClientTagFolder.java:151)
	at com.inductiveautomation.factorypmi.application.sqltags.ClientTagFolder.startBinding(ClientTagFolder.java:151)
	at com.inductiveautomation.factorypmi.application.sqltags.project.ProjectTagManager.startup(ProjectTagManager.java:474)
	at com.inductiveautomation.ignition.client.sqltags.impl.SubManagerAdapter.startup(SubManagerAdapter.java:277)
	at com.inductiveautomation.ignition.client.tags.impl.ClientTagManagerImpl.addClientTagProvider(ClientTagManagerImpl.java:165)
	at com.inductiveautomation.factorypmi.designer.model.VisionDesignerImpl.lambda$new$1(VisionDesignerImpl.java:325)
	at java.desktop/java.beans.PropertyChangeSupport.fire(Unknown Source)
	at java.desktop/java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.desktop/java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.desktop/java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at com.inductiveautomation.ignition.designer.DesignerContextImpl.fireScriptManagerInitialized(DesignerContextImpl.java:247)
	at com.inductiveautomation.ignition.designer.IgnitionDesigner.loadProject(IgnitionDesigner.java:1035)
	at com.inductiveautomation.ignition.designer.IgnitionDesigner$StartupProjectDialogHandler.lambda$new$2(IgnitionDesigner.java:2058)
	at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException: Cannot invoke "com.inductiveautomation.ignition.common.user.User.getRoles()" because "user" is null
	at com.inductiveautomation.ignition.client.expressions.ClientFunctionFactory$HasRoleFunctionClient.getRoles(ClientFunctionFactory.java:298)
	... 54 common frames omitted
10:44:16.045 [Designer-Startup] ERROR com.inductiveautomation.factorypmi.application.sqltags.project.ExpressionTagBinding -- Error executing expression for tag "testing".
com.inductiveautomation.ignition.common.expressions.ExpressionException: Error retrieving user from gateway.
	at com.inductiveautomation.ignition.client.expressions.ClientFunctionFactory$HasRoleFunctionClient.getRoles(ClientFunctionFactory.java:302)
	at com.inductiveautomation.ignition.client.expressions.ClientFunctionFactory$HasRoleFunctionClient.execute(ClientFunctionFactory.java:272)
	at com.inductiveautomation.ignition.client.expressions.ClientDynamicDispatchFunction.execute(ClientDynamicDispatchFunction.java:43)
	at com.inductiveautomation.ignition.common.expressions.FunctionExpression.execute(FunctionExpression.java:69)
	at com.inductiveautomation.factorypmi.application.sqltags.project.ExpressionTagBinding.childInteractionUpdated(ExpressionTagBinding.java:44)
	at com.inductiveautomation.factorypmi.application.sqltags.project.ExpressionTagBinding.startBinding(ExpressionTagBinding.java:33)
	at com.inductiveautomation.factorypmi.application.sqltags.project.ProjectTag.startBindingImpl(ProjectTag.java:202)
	at com.inductiveautomation.factorypmi.application.sqltags.project.ProjectTag.startBinding(ProjectTag.java:189)
	at com.inductiveautomation.factorypmi.application.sqltags.ClientTagFolder.startBinding(ClientTagFolder.java:151)
	at com.inductiveautomation.factorypmi.application.sqltags.ClientTagFolder.startBinding(ClientTagFolder.java:151)
	at com.inductiveautomation.factorypmi.application.sqltags.ClientTagFolder.startBinding(ClientTagFolder.java:151)
	at com.inductiveautomation.factorypmi.application.sqltags.project.ProjectTagManager.startup(ProjectTagManager.java:474)
	at com.inductiveautomation.ignition.client.sqltags.impl.SubManagerAdapter.startup(SubManagerAdapter.java:277)
	at com.inductiveautomation.ignition.client.tags.impl.ClientTagManagerImpl.addClientTagProvider(ClientTagManagerImpl.java:165)
	at com.inductiveautomation.factorypmi.designer.model.VisionDesignerImpl.lambda$new$1(VisionDesignerImpl.java:325)
	at java.desktop/java.beans.PropertyChangeSupport.fire(Unknown Source)
	at java.desktop/java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.desktop/java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at java.desktop/java.beans.PropertyChangeSupport.firePropertyChange(Unknown Source)
	at com.inductiveautomation.ignition.designer.DesignerContextImpl.fireScriptManagerInitialized(DesignerContextImpl.java:247)
	at com.inductiveautomation.ignition.designer.IgnitionDesigner.loadProject(IgnitionDesigner.java:1035)
	at com.inductiveautomation.ignition.designer.IgnitionDesigner$StartupProjectDialogHandler.lambda$new$2(IgnitionDesigner.java:2058)
	at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException: Cannot invoke "com.inductiveautomation.ignition.common.user.User.getRoles()" because "user" is null
	at com.inductiveautomation.ignition.client.expressions.ClientFunctionFactory$HasRoleFunctionClient.getRoles(ClientFunctionFactory.java:298)
	... 22 common frames omitted

If I make the default user source for the project the AD and then login with the same AD user as before, it works as expected. No errors.

So it seems that something is wonky with a fail over user source and how I'm doing the expression.

Any ideas?

It looks like I could also use the [System]Client/User/RolesString tag and do the following expression:

indexOf('Operator',coalesce({[System]Client/User/RolesString},'')) > -1

It is just strange that the other expression doesn't work as expected.

Does just invoking hasRole('Operator') work? The function shouldn't need an explicit username in the client, since you're always logged in as some user in Vision, unlike Perspective where you can be truly unauthenticated.

Yes, except when changing users it doesn't update the tag, which I would expect. Hence the reason I'm using the {[System]Client/User/Username} tag to trigger an update.

1 Like

Consider including it in a throwaway, like so (with my Integration Toolkit):

asList(
	{[System]Client/User/Username},
	hasRole('Operator')
)[1]

Something similar can be done with if() and duplicate branches.

1 Like

Another option would be to trick the expression into running at a particular rate with now, like now() && hasRole('Operator') would work I'm pretty sure, and just automatically poll every 1 second. The 'happy path' for hasRole with only one argument doesn't make an unnecessary RPC call so it'll return more or less instantly.

Actually... I think even just {[System]Client/User/Username} && hasRole('Operator') should work, or maybe len({[System]Client/User/Username}) > 0 && hasRole('Operator')? Somewhat awkward, and it would definitely be better if we weren't throwing the NPE, but that should get you the right result and behave the right way.

1 Like

Thanks for the ideas... I'll bounce them around and see what sticks.

1 Like