Code readingTroubleshooting

Barcode scanner property change script: fix the payload and navigate

Fix the barcode scanner property change script error 'int' object is unsubscriptable: read the payload type, guard the reset, navigate from the component.

LumaSpec Vision10 min read Share on LinkedIn

On this page 10 sections
  1. 01Symptom: the value updates, the log errors, navigation stays dead
  2. 02Cause 1: the change script runs twice, and the second payload is the integer 0
  3. 03Cause 2: a session property change has no page to navigate
  4. 04Cause 3: the page URL needs its leading slash
  5. 05Worked example: "Enamel Line 1" scanned from a docked header
  6. 06View or page navigation, and where parameters go
  7. 07The reader side: what arrives is what was sent
  8. 08Checks before you close the ticket
  9. 09Quick checklist
  10. 10Questions

A scan lands, the label bound to your session property flips from "Enamel Line 1" to "Enamel Line 2", and the log still collects a fresh TypeError: 'int' object is unsubscriptable on every scan — while the navigation you attached to that value never fires. All three observations are true at once, because three separate script problems are stacked on top of each other. This walkthrough is for the integrator wiring a barcode scanner input into a header that must work from any page, on Ignition Perspective 8.0.17 here. You will pin down the payload type the component delivers, place the navigation where a page exists, and leave with a guarded script you can copy.

Symptom: the value updates, the log errors, navigation stays dead

Three facts sit together and look contradictory. The label bound to the session property displays every scan correctly. The log shows Error running property change script on BarcodeScannerInput.props.data, ending in TypeError: 'int' object is unsubscriptable at line 1. And the navigation call, moved into the session property's own change script, throws a second traceback through the Perspective scripting functions instead of opening the view.

The scanner is not at fault. The component delivers its payload; the script mishandles one of the two shapes that property takes, and the navigation runs in a place that has no page. Work the causes in the order below and both errors stop.

Cause 1: the change script runs twice, and the second payload is the integer 0

The original script does three things: index into the incoming value, store a string on the session, then write self.props.data = 0 to clear the component for the next scan. That last line is the trap. Writing to a property fires the same property change script again, so one scan produces two runs:

  • Run 1: currentValue.value is the scan payload, a list such as ['Enamel Line 1']. The subscript works, the session property is set, the label updates.
  • Run 2: currentValue.value is the 0 the script just wrote. 0[0] has no meaning in Python, so the interpreter raises TypeError: 'int' object is unsubscriptable — worded 'int' object is not subscriptable in Python 3 — and the log takes one traceback per scan.

The traceback points at line 1 because that is where the subscript lives; the write that set up the failure is the last line of the same script. Prove the double-firing before changing anything: put one print at the top of the script and scan a single code.

print(type(currentValue.value), repr(currentValue.value))

One scan prints the two firings: <class 'list'> ['Enamel Line 1'], then <class 'int'> 0. The fix is a guard that lets the script act only on the scan pass:

raw = currentValue.value                # list on a scan, 0 on the reset
if isinstance(raw, list) and len(raw) > 0:
    item = raw[0]                       # 'Enamel Line 1'
    scanned = str(getattr(item, 'value', item))
    self.session.custom.barcode = scanned
    self.props.data = 0                 # fires run 2; the guard skips it

getattr(item, 'value', item) returns the element's value attribute when the component hands you qualified values, and the element itself when it hands you plain strings, so the script survives either payload shape.

Cause 2: a session property change has no page to navigate

Moving the navigation into the session property's change script trades one error for another. system.perspective.navigate works out which page to act on from the current page thread, and a session property change event is not attached to any page, so the call dies inside the scripting functions with the operateOnPage traceback.

The clean fix is to keep the navigation in the component's own script — the guarded script above — because the component lives in a view and the view lives in a page. That is exactly what a header needs: put the scanner input in a shared docked view, and the script has a page on every screen the operator opens, the mobile app included. Session properties still have a job — per-operator storage, one session per device, the pattern in barcode scanner session property per user, not a shared tag — but that job is storage, not navigation. If a value truly must stay session-level, the alternative is passing an explicit pageId argument to the navigation call; the write-up on resolving Perspective navigation in session property scripts covers that route in depth.

Cause 3: the page URL needs its leading slash

With the script moved, the navigation runs — and the device answers view not found. Page navigation addresses a page by the URL in its page configuration, and that URL must begin with a leading slash:

system.perspective.navigate(page='/enamel/lineview')

Copy the url exactly from the target page's configuration, slash included. One ordering detail from the same fix: anything that needs the page — logging included — belongs before the navigate call, because the page thread the script runs on goes away with the navigation.

Worked example: "Enamel Line 1" scanned from a docked header

The inputs, all from the case:

  • Component: barcode scanner input in a shared docked header view, operated from the mobile app.
  • Payload on a scan: ['Enamel Line 1'] — one list, length 1, holding one 13-character string.
  • Reset written by the script: 0, an int.
  • Target: the page with url /enamel/lineview, or the view enamel/lineview with LineNo as a parameter.

The rule: a property change script fires once per write to the property it watches, and must tolerate every type that property can hold. Trace one scan with the guarded script:

  1. Payload write: raw = ['Enamel Line 1'], len(raw) = 1, guard passes. item = 'Enamel Line 1', len(item) = 13, string stored on the session. startswith('Enamel') is true, so the component script navigates to /enamel/lineview. The last line writes self.props.data = 0.
  2. Reset write: raw = 0, isinstance(raw, list) is false, the guard skips the body. No subscript, no traceback.

The outcome: one scan, one payload pass, one skipped reset, nothing in the log, and the operator lands on the line view from whatever page they were on. The decision it buys you: a script that writes back to the property it watches has signed up for two events per scan, so guard the type or move the reset off that property. An unguarded script logs one traceback per scan, on every scan, for as long as it runs.

View or page navigation, and where parameters go

View navigation Page navigation
Call system.perspective.navigate(view='enamel/lineview', params={'LineNo': 1}) system.perspective.navigate(page='/enamel/lineview')
What changes the central view of the current page the whole page
Address bar stays as it was changes to the page url, leading slash required
Parameters params dictionary, received by name on the view carried by the page url and page configuration
Browser back button no page change to go back to returns to the previous page
Shared docked header unchanged unchanged

Both calls change only the primary, central panel, which is why a header scanner keeps working after every navigation: the shared dock is part of each page. Standardize on view navigation with params={'LineNo': 1} for header-driven detail views — the line number arrives as a named input parameter on the view, and a menu button uses the same call, so menus and barcode navigation share one parameter pattern with no extra session properties. Reserve page navigation for screens whose URL must change: deep links, bookmarks, back-button behavior. A screen that needs a different header is a different page with its own dock configuration, not a navigation argument.

The reader side: what arrives is what was sent

The component reports exactly what the physical reader delivers, so settle the payload at the source. A handheld scanner in keyboard mode sends keystrokes and a terminator — the setup path is in USB barcode scanner on a PLC HMI: HID keyboard mode or serial port, and prefix and suffix framing for barcode scanner input decides where one scan ends. A mounted reader pushes decoded strings over serial or Ethernet; framing and connection trouble there is a separate fix, covered in barcode scanner to an HMI serial port: framing data without headers and barcode reader to HMI over Ethernet: fix the connection.

For a station-mounted reader, the fixed code readers category covers linear and 2D codes at the line; a station that also inspects the part moves up to smart cameras. Three starting points:

Fixed code readersCompact fixed industrial barcode reader, 1.2 MP, USB 2.0, 60-120 mm reading distance (LS-CRD-0047)LS-CRD-0047
Resolution
Up to 3 MP
Shutter
Global
Interface
USB
$559.00View part
Fixed code readersHikrobot MV-ID3013PM-12M-WBN 12mm focal length 1.3 MP Compact Industrial Code ReaderMV-ID3013PM-12M-WBN
Interface
Fast Ethernet
Resolution
Up to 3 MP
$1,979.00View part
Fixed code readersHikrobot MV-ID813M-05S-WBN-SR 1.3 MP resolution, 4.9 mm focal length, White light, SR, GigE, Static cableMV-ID813M-05S-WBN-SR
Interface
GigE
Resolution
Up to 3 MP
$719.00View part

Glare on film-wrapped or glossy labels is fixed before the script with a polarizing cover or light board from code reader accessories, browsable in the accessories finder. If the reader uses laser illumination, keep its protective cover fitted — that cover is part of the laser class rating, not an optional part.

Checks before you close the ticket

  1. Scan the same code twice: two payload passes, two skipped resets, nothing new in the log.
  2. Scan a code that does not start with Enamel: the session property updates, no navigation fires, no error.
  3. From the mobile device, run the header from at least two different pages before signing it off.
  4. With page navigation, confirm the address bar shows the page url with its leading slash.
  5. Leave any page-dependent logging ahead of the navigate call, never after it.

More scanner-to-control wiring patterns are collected on the code reading topic page.

Quick checklist

  • Guard every change script that writes back to its own property: isinstance(raw, list) comes first.
  • Read the payload as raw[0], then getattr(item, 'value', item), then str.
  • Keep navigation in a component script; the component's page supplies the page thread.
  • Session properties store per-operator values — one session per device — and never host navigation.
  • Page navigation: copy the url from the page configuration, leading slash included, and call it last.

To match a reader to your labels and reading distances, filter the camera and reader finder by interface and resolution, or send the label samples through the contact page.

Questions

Why does the label still update when the script logs a TypeError?

The session property is written before the failing line ever runs. The traceback comes from the second firing, when the script's own reset has changed the property to the integer 0; by then the scan pass has already stored the string, so everything bound to the property shows the right text. The line number in the traceback points at the subscript, which misleads you into editing the wrong line.

Should the scanned value live in a session property or on the component?

Keep per-operator values there, but never host the navigation script there. Each device running the mobile app is its own session, so a session property gives every operator their own scanned value. The change script belongs on the component: the component sits in a view, the view in a page, so the navigation call always has a page thread. Passing pageId from a session script works but adds moving parts.

How do you pass a parameter with barcode navigation?

Use view navigation and hand values over in the params dictionary: params={'LineNo': 1} arrives as a named input parameter on the target view. Page navigation does not take that dictionary in the same way; its parameters travel in the page url and the page configuration. Standardize header menus and barcode navigation on the same view call with params, so one pattern serves both.

What does 'int' object is unsubscriptable actually mean?

It is Python telling you a number was indexed: 0[0] has no meaning, because integers are not subscriptable. In this component it does not indicate a scanner fault — the script indexed the incoming value on a firing where the property held the reset integer. Any property change script must tolerate every type that can land in the property it watches.

Does navigation change the docked header view?

No. View and page navigation both replace only the primary, central panel of the page. A shared docked header stays as configured on every page, which is why the header scanner keeps working after navigation. If a screen genuinely needs a different header, that is a separate page with its own dock configuration in the page setup, not an argument to the navigate call.