protected CompletableFuture<Map<S7StringVarLengthTag, StringSizes>> getStringSizes(PlcTagRequest request) {
        CompletableFuture<Map<S7StringVarLengthTag, StringSizes>> future = new CompletableFuture<>();

        // Build a request to read the length information for every var-length string in the request.
        List<S7StringVarLengthTag> varLengthStringTags = request.getTags().stream()
            .filter(plcTag -> plcTag instanceof S7StringVarLengthTag)
            .map(plcTag -> (S7StringVarLengthTag) plcTag)
            .collect(Collectors.toList());
        List<S7VarRequestParameterItem> stringFields = new ArrayList<>(varLengthStringTags.size());
        for (S7StringVarLengthTag varLengthStringTag : varLengthStringTags) {
            // For STRING, the header is 2 bytes (first byte contains the max length and the second the actual length)
            if (varLengthStringTag.getDataType() == TransportSize.STRING) {
                stringFields.add(new S7VarRequestParameterItemAddress(
                    new S7AddressAny(
                        TransportSize.BYTE,
                        2,
                        varLengthStringTag.getBlockNumber(),
                        MemoryArea.DATA_BLOCKS,
                        varLengthStringTag.getByteOffset(),
                        varLengthStringTag.getBitOffset()
                    )));
            }
            // For WSTRING, the header is 4 bytes (first word contains the max length and the second the actual length)
            else if (varLengthStringTag.getDataType() == TransportSize.WSTRING) {
                stringFields.add(new S7VarRequestParameterItemAddress(
                    new S7AddressAny(
                        TransportSize.BYTE,
                        4,
                        varLengthStringTag.getBlockNumber(),
                        MemoryArea.DATA_BLOCKS,
                        varLengthStringTag.getByteOffset(),
                        varLengthStringTag.getBitOffset()
                    )));
            } else {
                throw new PlcInvalidTagException("Only STRING and WSTRING allowed here.");
            }
        }
        final S7MessageRequest readRequest = new S7MessageRequest(
            getTpduId(), new S7ParameterReadVarRequest(stringFields), null);

        // Read the max length and actual size for each of the var-length strings.
        CompletableFuture<S7Message> resolveSizesRequestFuture = sendInternal(readRequest);
        resolveSizesRequestFuture.whenComplete((s7Message, throwable) -> {
            if (throwable != null) {
                future.completeExceptionally(new PlcProtocolException("Error resolving string sizes", throwable));
                return;
            }

            Map<S7StringVarLengthTag, StringSizes> stringLengths = new HashMap<>(varLengthStringTags.size());
            S7PayloadReadVarResponse getLengthsResponse = (S7PayloadReadVarResponse) s7Message.getPayload();
            int curItemIndex = 0;
            for (S7StringVarLengthTag varLengthStringTag : varLengthStringTags) {
                S7VarPayloadDataItem s7VarPayloadDataItem = getLengthsResponse.getItems().get(curItemIndex);
                // Something went wrong.
                // We simply don't save the length information, and then we'll treat it as INVALID_ADDRESS later on
                // in the code.
                if(s7VarPayloadDataItem.getReturnCode() != DataTransportErrorCode.OK) {
                    continue;
                }
                ReadBufferByteBased readBuffer = new ReadBufferByteBased(s7VarPayloadDataItem.getData());
                try {
                    if (varLengthStringTag.getDataType() == TransportSize.STRING) {
                        int maxChars = readBuffer.readUnsignedInt("maxLength", 8);
                        int actualChars = readBuffer.readUnsignedInt("maxLength", 8);
                        stringLengths.put(varLengthStringTag, new StringSizes(maxChars, actualChars));
                    } else if (varLengthStringTag.getDataType() == TransportSize.WSTRING) {
                        int maxChars = readBuffer.readUnsignedInt("maxLength", 16);
                        int actualChars = readBuffer.readUnsignedInt("maxLength", 16);
                        stringLengths.put(varLengthStringTag, new StringSizes(maxChars, actualChars));
                    } else {
                        throw new PlcInvalidTagException("Only STRING and WSTRING allowed here.");
                    }
                } catch (ParseException e) {
                    throw new PlcInvalidTagException("Error parsing var-length string actual lengths.");
                }
            }

            future.complete(stringLengths);
        });

        return future;
    }
A generic purpose error handler which terminates transaction and calls back given future with error message.
plc4j/drivers/s7/src/main/java/org/apache/plc4x/java/s7/readwrite/protocol/S7ProtocolLogic.java