# Read Tags, Check for Bad\_NotFound vs Exists

**URL:** <https://forum.inductiveautomation.com/t/read-tags-check-for-bad-notfound-vs-exists/95531>\
**Category:** Ignition\
**Tags:** ignition81, scripting\
**Created:** [September 4, 2024, 3:01pm UTC](https://forum.inductiveautomation.com/t/read-tags-check-for-bad-notfound-vs-exists/95531 "2024-09-04T15:01:04Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [September 4, 2024, 3:01pm UTC](https://forum.inductiveautomation.com/t/read-tags-check-for-bad-notfound-vs-exists/95531/1 "2024-09-04T15:01:04Z")

</div>

I have a handful of client tags that must exist for scripts to work properly. If they don't exist I will throw an error box.

My question is, at what point is it more efficient/better to use `system.tag.readblocking()` and check for Bad\_NotFound on all of the tags, instead of using `system.tag.exists()` for each tag?

i.e.

```python
if not system.tag.exists('tag1') or not system.tag.exists('tag2') or not system.tag.exists('tag3') or not system.tag.exists('tag4') or not system.tag.exists('tag5'):
	print "return"
else:
	print "continue"

```

vs

```python
tags = system.tag.readBlocking(['tag1','tag2','tag3','tag4','tag5'])

if True in [tag.quality >= 512 for tag in tags]:
	print "return"
else:
	print "continue"

```

assuming that the tags will need to be read to continue whatever code execution follows

---

<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:** [September 4, 2024, 3:27pm UTC](https://forum.inductiveautomation.com/t/read-tags-check-for-bad-notfound-vs-exists/95531/2 "2024-09-04T15:27:31Z")

</div>

I would say as soon as there are multiple tags readBlocking will be more performant.

Though, I would do it like this:

```python
qvs = system.tag.readBlocking(['tag1','tag2','tag3','tag4','tag5'])
if any(qv.quality.isBadOrError() for qv in qvs):
    #return, possibly with error
#otherwise do stuff.

```

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [September 4, 2024, 3:33pm UTC](https://forum.inductiveautomation.com/t/read-tags-check-for-bad-notfound-vs-exists/95531/3 "2024-09-04T15:33:20Z")

</div>

`exists()` internally _is_ just a read call; it's got some special handling to prefer reading the `.Name` property, which is lighter than reading the value of all tag types, but it's literally just a read call that checks the returned quality code.

So, technically, to get the exact same logic but more performant, you unfortunately do need an import:

```python
from com.inductiveautomation.ignition.common.model.values.QualityCode import Bad_NotFound

qvs = system.tag.readBlocking(['tag1','tag2','tag3','tag4','tag5'])
if any(qv.quality.is(Bad_NotFound) for qv in qvs):
    #return, possibly with error
#otherwise do stuff.

```

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [September 4, 2024, 3:37pm UTC](https://forum.inductiveautomation.com/t/read-tags-check-for-bad-notfound-vs-exists/95531/4 "2024-09-04T15:37:40Z")

</div>

without the import would checking for code 519 work?

Or, is there any actual difference between your `qv.quality.is(Bad_NotFound)` and @lrose 's `qv.quality.isBadOrError()` ?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [September 4, 2024, 3:45pm UTC](https://forum.inductiveautomation.com/t/read-tags-check-for-bad-notfound-vs-exists/95531/5 "2024-09-04T15:45:21Z")

</div>

`isBadOrError` will return true for _any_ bad or error quality code. So you wouldn't be able to distinguish between "the device is unavailable" and "the tag doesn't exist". That's the advantage of the more explicit check for the not found quality.

You can't directly pass 519, because that's not the actual underlying quality code value. The "actual value" of `Bad_NotFound` is `-2147483129`, because we pack semantic meaning into some of the 32 bits of a long int before we stuff the "user code" (519) at the end. I'd just do the import rather than peppering my code with a wacky number like that.

---

<div class="post-metadata">

**Author:** ![amarks](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/amarks/32/45610_2.png) [@amarks](https://forum.inductiveautomation.com/u/amarks)\
**Post date:** [September 4, 2024, 3:49pm UTC](https://forum.inductiveautomation.com/t/read-tags-check-for-bad-notfound-vs-exists/95531/6 "2024-09-04T15:49:13Z")

</div>

Definitely would prefer the import over a "magic number."

Since this is actually a client tag, I'll stick with the `isBadOrError`. I would assume `isBad` would work as well, but I'm guessing lrose went off the "\> 512" that I originally used.

---

<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:** [September 4, 2024, 3:57pm UTC](https://forum.inductiveautomation.com/t/read-tags-check-for-bad-notfound-vs-exists/95531/7 "2024-09-04T15:57:55Z")

</div>

`isBad()` will return true for any “Bad” condition. So it’s less specific than the import, but more specific than `isBadOrError()`
