# Timeseries Data Invalid Key Issue

**URL:** <https://forum.inductiveautomation.com/t/timeseries-data-invalid-key-issue/86825>\
**Category:** Ignition\
**Tags:** ignition81, perspective\
**Created:** [March 5, 2024, 2:17pm UTC](https://forum.inductiveautomation.com/t/timeseries-data-invalid-key-issue/86825 "2024-03-05T14:17:53Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![Shawn\_Rieger1](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/shawn_rieger1/32/57040_2.png) [@Shawn\_Rieger1](https://forum.inductiveautomation.com/u/Shawn_Rieger1)\
**Post date:** [March 5, 2024, 2:17pm UTC](https://forum.inductiveautomation.com/t/timeseries-data-invalid-key-issue/86825/1 "2024-03-05T14:17:53Z")

</div>

Morning! I have the following data in my time-series chart. The chart works. It seems to plot without issues; however, all my keys throw an invalid key error.

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/1/6/16853ed88f15c45198da8c585e65fe5b50baa91e.png)

I'm assuming this is due to the brackets, but this should be valid python

![image](https://us1.discourse-cdn.com/inductiveautomation/original/3X/7/6/76440d3c57937c438615962cf4d315b9c3138a01.png)

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [March 5, 2024, 2:20pm UTC](https://forum.inductiveautomation.com/t/timeseries-data-invalid-key-issue/86825/2 "2024-03-05T14:20:51Z")

</div>

Keys in Perspective objects must be valid for JSON. It isn't enough to be valid python, because Perspective doesn't keep python/jython values in their original form. No brackets or slashes or other punctuation in keys. If you couldn't use it as a jython variable name, you can't use it as a key in a Perspective object. (No leading digits, either.)

Keep in mind that things that "accidentally" work with errors present may not work with future upgrades.

---

<div class="post-metadata">

**Author:** ![Shawn\_Rieger1](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/shawn_rieger1/32/57040_2.png) [@Shawn\_Rieger1](https://forum.inductiveautomation.com/u/Shawn_Rieger1)\
**Post date:** [March 5, 2024, 2:53pm UTC](https://forum.inductiveautomation.com/t/timeseries-data-invalid-key-issue/86825/3 "2024-03-05T14:53:19Z")

</div>

Thanks Phil. I'm looking at the Columns property under trends and don't see a way I can specify a different name. I'd like the xtrace to show the tag path. This obviously works with a dataset but I can't use that in this instance. Do I have any other options to configure the column names that are displayed to the user?

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [March 5, 2024, 3:34pm UTC](https://forum.inductiveautomation.com/t/timeseries-data-invalid-key-issue/86825/4 "2024-03-05T15:34:55Z")

</div>

Make up series names (or munge the name into something valid).

Reconsider using dataset format for your series and supply the `columns` property in your trends.

---

<div class="post-metadata">

**Author:** ![Shawn\_Rieger1](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/shawn_rieger1/32/57040_2.png) [@Shawn\_Rieger1](https://forum.inductiveautomation.com/u/Shawn_Rieger1)\
**Post date:** [March 5, 2024, 3:37pm UTC](https://forum.inductiveautomation.com/t/timeseries-data-invalid-key-issue/86825/5 "2024-03-05T15:37:08Z")

</div>

I'm creating a 'live' trend using tag bindings, pushing their values to the series data each second, with a rolling 5-minute window. I figured this method would be more efficient than the immutable dataset.

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [March 5, 2024, 3:37pm UTC](https://forum.inductiveautomation.com/t/timeseries-data-invalid-key-issue/86825/6 "2024-03-05T15:37:39Z")

</div>

In my experience 90% of a tag path is useless to an end user.

I would use just the tag name and replce any spaces with an \_ character.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [March 5, 2024, 3:40pm UTC](https://forum.inductiveautomation.com/t/timeseries-data-invalid-key-issue/86825/7 "2024-03-05T15:40:15Z")

</div>

> [@Shawn\_Rieger1](#):
>
> I figured this method would be more efficient than the immutable dataset.

Maybe. If Ignition isn't just resending the whole column as you add values.

Have you tried my `recorder()` function from my Integration Toolkit?

---

<div class="post-metadata">

**Author:** ![Shawn\_Rieger1](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/shawn_rieger1/32/57040_2.png) [@Shawn\_Rieger1](https://forum.inductiveautomation.com/u/Shawn_Rieger1)\
**Post date:** [March 5, 2024, 3:42pm UTC](https://forum.inductiveautomation.com/t/timeseries-data-invalid-key-issue/86825/8 "2024-03-05T15:42:09Z")

</div>

Unfortunately, we have many tags with the same name under different device folders/paths.

---

<div class="post-metadata">

**Author:** ![Shawn\_Rieger1](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/shawn_rieger1/32/57040_2.png) [@Shawn\_Rieger1](https://forum.inductiveautomation.com/u/Shawn_Rieger1)\
**Post date:** [March 5, 2024, 3:50pm UTC](https://forum.inductiveautomation.com/t/timeseries-data-invalid-key-issue/86825/9 "2024-03-05T15:50:44Z")

</div>

> [@pturmel](#):
>
> Have you tried my `recorder()` function from my Integration Toolkit?

I have not! What is this Integration Toolkit you speak of? 🙂 Can you link me, please?

-- Update --

Found it! Thanks I will check this out.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [March 5, 2024, 4:06pm UTC](https://forum.inductiveautomation.com/t/timeseries-data-invalid-key-issue/86825/10 "2024-03-05T16:06:27Z")

</div>

Hint: You'll probably want to feed the `recorder()` with an expression involving `tags()`...

---

<div class="post-metadata">

**Author:** ![Shawn\_Rieger1](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/shawn_rieger1/32/57040_2.png) [@Shawn\_Rieger1](https://forum.inductiveautomation.com/u/Shawn_Rieger1)\
**Post date:** [March 5, 2024, 4:17pm UTC](https://forum.inductiveautomation.com/t/timeseries-data-invalid-key-issue/86825/11 "2024-03-05T16:17:50Z")

</div>

Just going over this module. Is the idea that the expressions are more performant than scripting?

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [March 5, 2024, 4:41pm UTC](https://forum.inductiveautomation.com/t/timeseries-data-invalid-key-issue/86825/12 "2024-03-05T16:41:28Z")

</div>

For what it's worth, we have a similar situation, so I use something like this:

```auto
from com.inductiveautomation.ignition.common.tags.paths.parser import TagPathParser

def sanitizePenNames(pens,path):
    pathParts = TagPathParser.chopPath(path)

    def generatePenName(parts, cursor = 0):
        cursor -= 1
        #sanitize
        parts = [part.replace(' ','_') for part in parts]
        penName = '_'.join(parts[cursor:])

       if penName in pens:
            return generatePenName(parts,cursor)
        return penName
    return generatePenName(pathParts)

```

This converts the path into a list of its parts(excluding the provider) and then recursively walk from the tail of the list until a unique name is found. So, for example, if your pens was `['tag_name'] `and you wanted to add a pen for `'[provider]path/to/tag name'` then the function would return `'to_tag_name'`.

> [@Shawn\_Rieger1](#):
>
> Is the idea that the expressions are more performant than scripting?

Less of an idea and more of a fact.

Phil's module is awesome.
