1. Problem Summary
The OPC UA client receives Bad_NoSubscription (status code 0x80210000) as a service result on the PublishResponse even though the client application believes it has one or more active Subscriptions registered with the server. The symptom is observed against:
- Siemens S7-1500 CPU OPC UA server (firmware V1.8 through V2.x families)
- Siemens S7-1200 CPU OPC UA server (firmware V4.2 and later with activated OPC UA)
- Third-party OPC UA gateways front-ending S7-1500/S7-1200 (for example Kepware 5.20)
Typical log output from a Prosys OPC UA Java SDK client:
2017-03-24 10:59:34,894 DEBUG [Blocking-Work-Executor-17] client.UaClient handlePublishResponse: response=0
2017-03-24 10:59:34,894 INFO [Blocking-Work-Executor-17] client.UaClient$a Got Bad_NoSubscription as a PublishResponse, although we have connected Subscriptions
2017-03-24 10:59:34,997 DEBUG [Blocking-Work-Executor-21] client.UaClient handlePublishResponse: response=0
2017-03-24 10:59:34,997 INFO [Blocking-Work-Executor-21] client.UaClient$a Got Bad_NoSubscription as a PublishResponse, although we have connected Subscriptions
The repeat rate is often millisecond-scale (10–200 ms between lines) which means the client is not recovering on its own. Each redundant PublishRequest in the client's queue is bounced back with Bad_NoSubscription, blocking notifications and inflating traffic.
2. OPC UA Subscription and Publish Model (Spec Background)
The mechanism that produces Bad_NoSubscription is defined by the OPC UA specification, Part 4 – Services. The relevant clauses are:
| Spec Reference | Clause | Requirement |
|---|---|---|
| OPC UA Part 4 | 5.13.1 CreateSubscription | Server returns a subscriptionId that the client must reference in every subsequent PublishRequest. |
| OPC UA Part 4 | 5.13.3 ModifySubscription | Server may return Bad_SubscriptionIdInvalid if the id is unknown. |
| OPC UA Part 4 | 5.13.4 DeleteSubscriptions | "Successful completion of this Service causes all MonitoredItems that use the Subscription to be deleted. If this is the last Subscription for the Session, then all Publish requests still queued for that Session are de-queued and shall be returned with Bad_NoSubscription." |
| OPC UA Part 4 | 5.13.5 SetPublishingMode | Disabling a Subscription may cause queued Publish requests to be discarded. |
| OPC UA Part 4 | 5.14 Publish | Server returns Bad_NoSubscription when no subscription in the session matches the publish request. |
Reference: OPC UA Part 4 specification (Services), OPC Foundation.
The publish queue is a per-session buffer that the client keeps filled. The server responds to each PublishRequest in FIFO order with either a notification message or a KeepAlive if no notifications are pending. When the server has zero subscriptions matching the session, every queued PublishRequest is de-queued and answered with Bad_NoSubscription. The client must therefore guarantee that any PublishRequest it has outstanding corresponds to a live subscription on the server.
3. Root Cause Analysis
The status code itself is correct behaviour from a strictly compliant OPC UA server. The condition that triggers it on S7-1500 / S7-1200 is one of the following desynchronisation states:
3.1 Empty Subscription Added Before Any MonitoredItem
The Java code pattern most often associated with the symptom is:
Subscription subscription = new Subscription();
subscription.addNotificationListener(this.subscriptionNotificationListener);
client.addSubscription(subscription); // Subscription registered with 0 MonitoredItems
The Prosys SDK ships the subscription to the server immediately. Some S7-1500 firmware builds (notably V1.8 through V2.0 with limited OPC UA session resources) reject, evict, or fail to retain a subscription that has zero MonitoredItems attached at the moment of CreateSubscription. The client's internal model still references the original subscriptionId; the server's session table does not. The client's queue of PublishRequest calls therefore never matches a real subscription.
3.2 Subscription Removed or Cleared Mid-Session
If a subscription is added, populated, then later emptied of all MonitoredItems and never deleted, the server may garbage-collect the empty subscription while the client retains the handle. The same loop of PublishRequest → Bad_NoSubscription follows.
3.3 Server-Side Restart or TIA Portal Download
A PLC stop/start, a TIA Portal "Download to device", a firmware update, or an OPC UA server option-bit toggle invalidates the session. The PLC drops all subscriptions silently. The client only discovers the desynchronisation when its next PublishRequest returns Bad_NoSubscription. If the client does not react to this status code with a full DeleteSubscriptions + reconnect, the loop persists.
3.4 Network-Induced Session Timeout
The S7-1500 default SessionTimeout is 60,000 ms. If the client misses two consecutive keep-alives (timeout = 2 × publishingInterval minimum) the server closes the session and discards the subscriptions. The client's UaSession object still believes the channel is open until its next operation fails.
3.5 Kepware Channel Reconnect
When Kepware 5.20 is front-ending the S7-1500/1200, a channel reconnect drops all internal subscriptions without sending a ServiceFault for queued publish requests. The S7-1500-side session is rebuilt; the client-side session is not, producing the same Bad_NoSubscription cascade.
4. Affected SDK Versions and PLC Firmware
| Prosys Java SDK Version | Behaviour | Recommended Action |
|---|---|---|
| 2.2.2 | Reproducible Bad_NoSubscription loop with empty subscription on S7-1500. | Upgrade to ≥ 2.2.7. |
| 2.2.6 | Improved subscription handling; less prone but still vulnerable. | Apply MonitoredItem-first pattern as defence in depth. |
| 2.2.7 | Confirmed working workaround: at least one valid MonitoredDataItem must be added before client.addSubscription(). |
Adopt as minimum baseline. |
| 2.3.0 | Substantial changes to subscription state machine. | Re-validate against S7-1500 firmware V2.5+. |
| 2.3.2-781 | Still reproduces with S7-1200 + Kepware 5.20 combination. | Apply reconnection handler, see §7. |
| Siemens CPU | Firmware Range | OPC UA Server Status | Notes |
|---|---|---|---|
| S7-1500 | V1.8 – V2.0 | Activated by license | Most aggressive eviction of empty subscriptions. |
| S7-1500 | V2.1 – V2.5 | Activated | Improved, but empty-subscription still unreliable. |
| S7-1500 | V2.6+ | Activated | More tolerant; pattern still recommended. |
| S7-1200 | V4.2 – V4.4 | Activated as of V4.2 | Requires at least one valid MonitoredItem. |
| S7-1200 | V4.5+ | Activated | More robust, but no guarantee across TIA Portal re-downloads. |
Reference: Siemens Industry Online Support – S7-1500 OPC UA server documentation.
5. Diagnostic Procedure
5.1 Capture a Network Trace
- Install Wireshark with the OPC UA dissector.
- Capture the TCP conversation to the PLC (default TCP/4840, sometimes TCP/4845 for redundant pairs).
- Filter on
opcuaand look for thePublishRequest/PublishResponsepairs. - Confirm the response carries
StatusCode.ServiceResult = 0x80210000 (Bad_NoSubscription). - Compare the
SubscriptionIdin the request against the most recentCreateSubscriptionResponse– if they match, the client believes the subscription is alive but the server has already deleted it.
5.2 Enable Prosys SDK Tracing
Add the following to logback.xml:
<logger name="com.prosysopc.ua.client.UaClient" level="DEBUG"/>
<logger name="com.prosysopc.ua.client.Subscription" level="DEBUG"/>
<logger name="com.prosysopc.ua.transport.tcp.impl.SecurityTokenListener" level="DEBUG"/>
Reference: Prosys OPC UA Java SDK – logging configuration.
5.3 TIA Portal Server Diagnostics
- In TIA Portal, navigate to CPU Properties → Communication → OPC UA.
- Open the Diagnostics view on the PLC web server (https://<plc-ip>/diagnostics) for live session counts.
- Count the active subscriptions. If the count is zero but the client log shows non-zero, the desynchronisation is confirmed.
Reference: Siemens – S7-1500 OPC UA server diagnostics entry ID 109769500.
6. Workaround – Populate Subscription Before Registering
The most reliable workaround documented for the Prosys SDK 2.2.x series is to add at least one valid MonitoredItem to the subscription before registering the subscription with the client:
// Step 1: build the subscription
Subscription subscription = new Subscription();
subscription.setPublishingInterval(250.0); // ms
subscription.setSamplingInterval(100.0); // ms
subscription.setPublishingEnabled(true);
subscription.addNotificationListener(notificationListener);
// Step 2: create a MonitoredItem BEFORE registering the subscription
UaMonitoredItem item = new UaMonitoredItem(
client.getAddressSpace().getNode(NODE_ID), // any valid variable
AttributeId.Value,
MonitoringMode.Reporting,
new MonitoringParameters());
item.setMonitoringMode(MonitoringMode.Reporting);
subscription.addItem(item);
// Step 3: only now register the subscription
client.addSubscription(subscription);
// Step 4: attach the items via the SDK
client.registerMonitoredItems(Collections.singletonList(item));
This pattern keeps the subscription non-empty for the entire lifetime of its CreateSubscription + CreateMonitoredItems sequence on the server, eliminating the empty-subscription eviction race.
NODE_ID with a real NodeId from the S7-1500 address space (for example ns=1;s="DB1"."Var1"). The PLC will not retain a subscription for a non-existent node.
7. Reconnection Strategy and KeepAlive Recovery
Even with the population workaround, the client must react to Bad_NoSubscription as a hard signal to rebuild its state. Implement a guarded reconnection listener:
private final ScheduledExecutorService watchdog =
Executors.newSingleThreadScheduledExecutor();
void onBadNoSubscription() {
watchdog.schedule(() -> {
try {
logger.warn("Bad_NoSubscription detected; rebuilding session");
client.disconnect();
client.connect();
rebuildAllSubscriptions(); // creates new CreateSubscription
} catch (Exception e) {
logger.error("Reconnection failed", e);
}
}, 250, TimeUnit.MILLISECONDS);
}
private void rebuildAllSubscriptions() throws ServiceResultException {
for (Subscription s : client.getSubscriptions().values()) {
client.removeSubscription(s);
}
// re-add with at least one MonitoredItem (see §6)
recreateFromConfig();
}
7.1 Keep-Alive Tuning
| Parameter | Recommended Value for S7-1500 | Rationale |
|---|---|---|
| PublishingInterval | 250 ms | Below default keep-alive, but above S7-1500 minimum of 100 ms. |
| SamplingInterval | 100 ms | Lowest value honoured by the PLC. |
| KeepAliveCount | 10 | 10 × 250 ms = 2.5 s of buffer for network jitter. |
| LifeTimeCount | 30 | 30 × 250 ms = 7.5 s before the server drops the subscription. |
| MaxNotificationsPerPublish | 1000 | S7-1500 limit; values above return Bad_TooManyOperations. |
| Priority | 1 (highest) | Ensures notifications are queued before low-priority subscriptions. |
7.2 ServiceResult Inspection
Subscribe to the SDK's UaClient.ServiceResponseListener and inspect every ServiceResponse:
client.addServiceResponseListener((request, response) -> {
if (response.getServiceResult().getValue() == StatusCode.Bad_NoSubscription) {
onBadNoSubscription();
}
});
Reference: Prosys OPC UA Java SDK – Client API documentation.
8. Server-Side Hardening (TIA Portal OPC UA Settings)
- Open the CPU in TIA Portal.
- Navigate to Properties → Communication → OPC UA → Server.
- Set Minimum publishing interval to 100 ms.
- Set Minimum sampling interval to 100 ms.
- Set Maximum subscriptions per session to 10 (matches typical client use, prevents resource exhaustion).
- Set Maximum MonitoredItems per subscription to 2000.
- Enable Security policies at minimum
Basic128Rsa15orBasic256Sha256. - Confirm Server interface = "Activate OPC UA Server" is checked.
- Compile and download the hardware configuration to the PLC.
Reference: Siemens – S7-1500 OPC UA server configuration entry ID 109769500.
Bad_NoSubscription cascade. Schedule the change during a maintenance window.
9. Kepware-Specific Configuration
When front-ending an S7-1500/1200 with Kepware 5.20:
- Open Kepware Configuration.
- Right-click the channel → Properties → Advanced.
- Set OPC UA Server Max Subscription Lifetime to 30000 ms.
- Set OPC UA Server Max Keep-Alive Count to 10.
- Set Reconnect Enabled = True.
- Set Reconnect Interval = 5000 ms.
Reference: Kepware KEPServerEX – OPC UA server settings documentation.
10. Long-Term Mitigation
- Upgrade the Prosys Java SDK to the latest 5.x line. The 2.x SDK is no longer maintained; new versions ship explicit handling for
Bad_NoSubscriptionandBad_SubscriptionIdInvalidthat auto-rebuilds state. - Move from empty-then-populate to populate-then-register as a coding standard for any OPC UA client against S7-1500/1200.
- Implement a watchdog that fires every
LifeTimeCount × PublishingIntervaland verifies the subscription is still healthy by writing a dummy value to a known tag. - Wrap every subscription operation in a circuit breaker that pauses new
PublishRequestissuance for 5 s after receiving three consecutiveBad_NoSubscriptionerrors.
11. Verification Checklist
| Check | Pass Criteria |
|---|---|
| Subscription count on server vs client | Match at all times. |
Network trace PublishResponse codes |
Always Good or Good_LocalOverride; never Bad_NoSubscription. |
| PLC web server Diagnostics | Active subscriptions ≥ 1 while client is connected. |
| Client log | Zero Got Bad_NoSubscription as a PublishResponse lines over 24 h soak. |
| TIA Portal Online → OPC UA → Server → Status | SessionState = Running, LastError = None. |
| Wireshark heartbeat | KeepAlive response within 2.5 s of request. |
12. Troubleshooting Matrix
| Symptom | Likely Cause | First Check |
|---|---|---|
Single Bad_NoSubscription at startup |
Server wiped queued PublishRequests on session creation (spec-compliant). | Confirm it occurs once and stops. |
Continuous Bad_NoSubscription in a tight loop |
Empty subscription or TIA Portal download. | Re-apply the §6 workaround and download the PLC config. |
Bad_NoSubscription after network blip |
Session timeout expired server-side, client not aware. | Enable watchdog per §7. |
| Only seen against S7-1500 firmware V1.8 | Empty-subscription eviction bug. | Upgrade CPU firmware to V2.6+ and SDK to ≥ 2.2.7. |
| Only seen against S7-1200 + Kepware 5.20 | Kepware reconnect drops internal subscription state. | Tune Kepware per §9. |
Bad_NoSubscription after SCADA restart |
SCADA reissued CreateSubscription before PLC finished cleaning the old session. | Add 2 s delay between disconnect and reconnect. |
Bad_NoSubscription alternating with Good
|
Multiple clients on one session or session multiplexing bug. | Verify one subscription per session per spec. |
FAQ
What does OPC UA status code 0x80210000 (Bad_NoSubscription) mean?
It means the server received a PublishRequest whose subscriptionId does not match any active subscription on the session. Per OPC UA Part 4 §5.13.8.1, queued PublishRequests are answered with Bad_NoSubscription when the last subscription for a session is deleted, or when the server has already purged the subscription.
Why does the S7-1500 return Bad_NoSubscription even when my client believes a subscription is active?
The most common cause on S7-1500 firmware V1.8–V2.5 is that the client registered a subscription before adding any MonitoredItem. The PLC evicts empty subscriptions at creation time. The client's internal handle still references a subscriptionId that the server has already discarded. Add at least one valid MonitoredItem before calling client.addSubscription().
Which Prosys Java SDK version is safe for S7-1500 OPC UA communication?
Prosys SDK 2.2.7 introduced a working workaround when at least one MonitoredItem is added prior to subscription registration. Versions 2.3.0 and 2.3.2-781 still reproduce the symptom under specific configurations (notably S7-1200 behind Kepware 5.20). The recommended action is to upgrade to the latest 5.x SDK line, which handles Bad_NoSubscription automatically.
Can I rely on the TIA Portal publishing interval settings to fix Bad_NoSubscription?
No. Adjusting the publishing interval only governs how often notifications are generated. The Bad_NoSubscription condition is a session-level subscription lifecycle mismatch; it is independent of publishing interval and is fixed at the client code or SDK upgrade level.
How do I detect Bad_NoSubscription in a running OPC UA client without parsing log files?
Register a ServiceResponseListener on the UaClient and inspect every ServiceResponse. When response.getServiceResult().getValue() equals StatusCode.Bad_NoSubscription, trigger a controlled reconnect. Combine this with a watchdog that fires at LifeTimeCount × PublishingInterval to catch silent desynchronisation.