S7: reading a BOOL array fails with NullPointerException in PlcBOOL.of, tag returns INTERNAL_ERROR
- Dominant language
- Java
- Stars
- 1.7k
- Forks
- 504
- Avg merge
- 10h 55m
- Merged PRs (30d)
- 43
Description
## What happens
Reading a `BOOL` array tag from a non-optimized data block on an S7-1500 fails. The tag comes back
with response code `INTERNAL_ERROR` and no value; the driver logs a `NullPointerException` while
decoding. Scalar `BOOL` reads from the same block work, and so do arrays of every other type I
tried (`BYTE`, `INT`, `UINT`, `DINT`, `LREAL`, `TIME`, `STRING`).
## Versions
- PLC4X 1.0.0 (`plc4j-driver-s7`, `plc4j-spi-values`, `plc4j-spi-drivers`)
- Java 21, macOS 15 (aarch64)
- Device: S7-1500 (CPU 1511-C PN), classic protocol, non-optimized block access
## Steps to reproduce
Read an array of BOOL from a non-optimized DB:
```java
PlcConnection connection = new DefaultPlcDriverManager().getConnection("s7://192.168.0.1");
PlcReadRequest request = connection.readRequestBuilder()
.addTagAddress("bools", "%DB42:214.0[1..8;1]:BOOL")
.build();
PlcReadResponse response = request.execute().get();
System.out.println(response.getResponseCode("bools")); // INTERNAL_ERROR
```
`%DB42:216.0[0..7]:BYTE` against the same block succeeds, as does the scalar `%DB42:0.0:BOOL`,
so the connection and the block itself are fine.
## Stack trace
```
WARN o.apache.plc4x.java.s7.S7CotpConnection : Error decoding tag %DB42:214.0[1..8;1]:BOOL
java.lang.NullPointerException: Cannot invoke "Object.toString()" because "value" is null
at org.apache.plc4x.java.spi.values.PlcBOOL.of(PlcBOOL.java:65)
at org.apache.plc4x.java.spi.values.DefaultPlcValueHandler.ofElement(DefaultPlcValueHandler.java:139)
at org.apache.plc4x.java.spi.values.DefaultPlcValueHandler.ofElements(DefaultPlcValueHandler.java:116)
at org.apache.plc4x.java.spi.values.DefaultPlcValueHandler.of(DefaultPlcValueHandler.java:86)
at org.apache.plc4x.java.s7.S7CotpConnection.parsePlcValue(S7CotpConnection.java:995)
at org.apache.plc4x.java.s7.S7CotpConnection.decodeBindingInto(S7CotpConnection.java:736)
at org.apache.plc4x.java.s7.S7CotpConnection.applyChunkResponse(S7CotpConnection.java:699)
at org.apache.plc4x.java.s7.S7CotpConnection.lambda$sendReadChunk$1(S7CotpConnection.java:649)
at java.base/java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:955)
...
at org.apache.plc4x.java.spi.drivers.throttle.RequestThrottle.lambda$start$0(RequestThrottle.java:121)
```
## Analysis
Two things seem to be in play, and I suspect only the first is the actual bug:
1. **The element values reaching the value handler are null.** `DefaultPlcValueHandler.ofElements`
iterates the `Object[]` it is handed and calls `ofElement` per entry, so at least one entry is
null by the time it gets there. That points at the S7 side (`S7CotpConnection.parsePlcValue` /
`decodeBindingInto`) producing a shorter or sparsely-filled value array for packed BOOL arrays
than the declared element count — plausible since BOOLs are bit-packed and every other,
byte-aligned, array type decodes correctly. I have not confirmed this in a debugger, so treat
the exact mechanism as a guess; the null element itself is certain from the trace.
2. **`PlcBOOL.of(Object)` has no null guard.** Its chain of `instanceof` checks all fail for null
and it falls through to `return new PlcBOOL(value.toString())`, which is where the NPE is
thrown. Even once the S7 decoding is fixed, a null here would be better off as a clear
`PlcInvalidTagException`/`IllegalArgumentException` than an NPE — the other `Plc*.of` factories
share this shape, so it is a general robustness point rather than something specific to BOOL.
## Impact
Because the exception is caught and mapped to a bare `INTERNAL_ERROR` response code, and PLC4X
exposes no per-tag exception on `PlcReadResponse`, a caller sees only the enum constant — the
stack trace is reachable only in the driver's own log. Surfacing the cause per tag (an optional
`Throwable` alongside the response code) would make failures like this far easier to diagnose,
though that is a separate enhancement from the bug above.
Contributor guide
Research direction
Start with S7CotpConnection.parsePlcValue and decodeBindingInto, then follow DefaultPlcValueHandler.ofElements and PlcBOOL.of using the supplied reproduction. Compare packed BOOL array handling with scalar BOOL and byte-aligned arrays. Done means the BOOL array returns its values instead of INTERNAL_ERROR, with coverage for this regression.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- embedded-iot
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 58/100