# How do you read a Perspective Session Custom Property from a Gateway Scoped Module?

**URL:** <https://forum.inductiveautomation.com/t/how-do-you-read-a-perspective-session-custom-property-from-a-gateway-scoped-module/113064>\
**Category:** Module Development\
**Tags:** ignition83, perspective\
**Created:** [January 4, 2026, 10:41pm UTC](https://forum.inductiveautomation.com/t/how-do-you-read-a-perspective-session-custom-property-from-a-gateway-scoped-module/113064 "2026-01-04T22:41:31Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![robert.t](https://avatars.discourse-cdn.com/v4/letter/r/a183cd/32.png) [@robert.t](https://forum.inductiveautomation.com/u/robert.t)\
**Post date:** [January 4, 2026, 10:41pm UTC](https://forum.inductiveautomation.com/t/how-do-you-read-a-perspective-session-custom-property-from-a-gateway-scoped-module/113064/1 "2026-01-04T22:41:31Z")

</div>

Hello,

I am trying to retrieve a perspective session custom property from a gateway scoped module and I have tried to use an InternalSession like below with no success.

```python
UUID internalUUID = InternalSession.SESSION.get().getSessionId();

PerspectiveContext pc = InternalSession.SESSION.get().getPerspectiveContext();

Optional<QualifiedValue> temp = pc.getSessionMonitor().findSession(internalUUID).get().getPropertyTreeOf(PropertyType.custom).read("custom.test123");

```

I was thinking that I could read the properties using this InternalSession, which has not worked, so I would like some advice or pointers to read the sessions custom property.

---

<div class="post-metadata">

**Author:** ![Kevin.Herron](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/kevin.herron/32/7259_2.png) [@Kevin.Herron](https://forum.inductiveautomation.com/u/Kevin.Herron)\
**Post date:** [January 4, 2026, 11:32pm UTC](https://forum.inductiveautomation.com/t/how-do-you-read-a-perspective-session-custom-property-from-a-gateway-scoped-module/113064/2 "2026-01-04T23:32:16Z")

</div>

I'm using this question to test a prompt, so excuse me ahead of time for answering you with obvious LLM output.

* * *

Reading Perspective session custom properties from Gateway-scoped code requires:

1. **Obtaining the PerspectiveContext** via `PerspectiveContext.get(GatewayContext)`
2. **Finding the session** via `SessionMonitor.findSession(UUID)` or using the `InternalSession.SESSION` ThreadLocal if executing within a session context
3. **Accessing the property tree** via `session.getPropertyTreeOf(PropertyType.custom)`
4. **Reading properties on the queue** - PropertyTree access MUST be done on the session's execution queue

The forum user's approach was close but likely failed because PropertyTree reads must be executed on the session's queue, not directly on any thread.

## Correct Implementation Pattern

### From Gateway Code with Known Session UUID

```java
import com.inductiveautomation.ignition.gateway.model.GatewayContext;
import com.inductiveautomation.perspective.gateway.api.PerspectiveContext;
import com.inductiveautomation.perspective.gateway.session.InternalSession;
import com.inductiveautomation.perspective.gateway.property.PropertyTree;
import com.inductiveautomation.perspective.common.api.PropertyType;
import com.inductiveautomation.ignition.common.QualifiedValue;

import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ExecutionException;

public Optional<QualifiedValue> readSessionCustomProperty(
        GatewayContext gatewayContext,
        UUID sessionId,
        String propertyPath) {

    // 1. Get PerspectiveContext
    PerspectiveContext perspectiveContext = PerspectiveContext.get(gatewayContext);

    // 2. Find the session by UUID
    Optional<InternalSession> sessionOpt = perspectiveContext
        .getSessionMonitor()
        .findSession(sessionId);

    if (sessionOpt.isEmpty()) {
        return Optional.empty();
    }

    InternalSession session = sessionOpt.get();

    // 3. Get the custom property tree
    PropertyTree customTree = session.getPropertyTreeOf(PropertyType.custom);
    if (customTree == null) {
        return Optional.empty();
    }

    // 4. Read property ON THE SESSION'S QUEUE (critical!)
    try {
        return customTree.getQueue()
            .runOrSubmit(() -> customTree.read(propertyPath))
            .get();
    } catch (InterruptedException | ExecutionException e) {
        return Optional.empty();
    }
}

```

### From Within Session Context (ThreadLocal available)

When code executes within a session context (script execution, binding evaluation), the ThreadLocal is available:

```java
public Optional<QualifiedValue> readCurrentSessionCustomProperty(String propertyPath) {
    // Get session from ThreadLocal
    InternalSession session = InternalSession.SESSION.get();
    if (session == null) {
        throw new IllegalStateException("No perspective session attached to this thread.");
    }

    PropertyTree customTree = session.getPropertyTreeOf(PropertyType.custom);
    if (customTree == null) {
        return Optional.empty();
    }

    // Read property on the session's queue
    try {
        return customTree.getQueue()
            .runOrSubmit(() -> customTree.read(propertyPath))
            .get();
    } catch (InterruptedException | ExecutionException e) {
        return Optional.empty();
    }
}

```

---

<div class="post-metadata">

**Author:** ![robert.t](https://avatars.discourse-cdn.com/v4/letter/r/a183cd/32.png) [@robert.t](https://forum.inductiveautomation.com/u/robert.t)\
**Post date:** [January 4, 2026, 11:54pm UTC](https://forum.inductiveautomation.com/t/how-do-you-read-a-perspective-session-custom-property-from-a-gateway-scoped-module/113064/3 "2026-01-04T23:54:28Z")

</div>

Thank you very much for your quick and detailed response.

I will give them a go now and report back here.

---

<div class="post-metadata">

**Author:** ![robert.t](https://avatars.discourse-cdn.com/v4/letter/r/a183cd/32.png) [@robert.t](https://forum.inductiveautomation.com/u/robert.t)\
**Post date:** [January 5, 2026, 1:46am UTC](https://forum.inductiveautomation.com/t/how-do-you-read-a-perspective-session-custom-property-from-a-gateway-scoped-module/113064/4 "2026-01-05T01:46:54Z")

</div>

I tested out the two methods that you had suggested and I was getting Optional.empty which I was not expecting.

I looked into this (for the Gateway Code) and did some tests and found that the PropertyTree does have the value that I’m looking for, but does not appear to be getting parsed correctly.

When I test with:

```python
customTree.getQueue().runOrSubmit(customTree::toJson).get().toString()

```

I get the result:

```python
{"$":["map",0,1767577165100],"test123":{"$":["qv",0,1767577165100],"$v":"321tset"}}

```

My session custom property looks like this:

 ![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/4/144eb4652acd1137e5dcedd72be82ebce5341127.png)

Is there something that needs to be done for the customTree.read(propertyPath) to be able to read the mapped value?

---

<div class="post-metadata">

**Author:** ![robert.t](https://avatars.discourse-cdn.com/v4/letter/r/a183cd/32.png) [@robert.t](https://forum.inductiveautomation.com/u/robert.t)\
**Post date:** [January 5, 2026, 1:56am UTC](https://forum.inductiveautomation.com/t/how-do-you-read-a-perspective-session-custom-property-from-a-gateway-scoped-module/113064/5 "2026-01-05T01:56:48Z")

</div>

I did some more digging and found that the path only needed to include the name of the value that I had under the session custom section.

So instead of:

```python
session.custom.test123

```

I used:

```python
test123

```

Thank you very much for your help Kevin.
