@@ -154,13 +154,35 @@ private void terminateAndPurgeOrchestration(String orchestrationInstanceId) {
154154 }
155155
156156 this .durableTaskClient .purgeInstance (orchestrationInstanceId );
157- } catch (IllegalArgumentException e ) {
158- // Instance not found — already deleted or never existed
159- logger .info ("Orchestration instance '" + orchestrationInstanceId +
160- "' is already purged or never existed." );
161157 } catch (Exception e ) {
162- logger .log (Level .WARNING ,
163- "Failed to terminate/purge orchestration: " + orchestrationInstanceId , e );
158+ if (isNotFound (e )) {
159+ // Orchestration instance doesn't exist — already deleted or never existed.
160+ logger .info ("Orchestration instance '" + orchestrationInstanceId
161+ + "' is already purged or never existed." );
162+ } else {
163+ logger .log (Level .WARNING ,
164+ "Failed to terminate/purge orchestration: " + orchestrationInstanceId , e );
165+ }
166+ }
167+ }
168+
169+ /**
170+ * Detects "instance not found" responses from the sidecar without taking a hard dependency on
171+ * gRPC types. The Durable Task gRPC client surfaces "not found" as a {@code StatusRuntimeException}
172+ * with code {@code NOT_FOUND}; we match against the exception's {@code toString()} which embeds
173+ * both. This mirrors .NET's {@code catch (RpcException ex) when (ex.StatusCode == StatusCode.NotFound)}.
174+ */
175+ private static boolean isNotFound (Throwable t ) {
176+ while (t != null ) {
177+ String name = t .getClass ().getName ();
178+ if ("io.grpc.StatusRuntimeException" .equals (name ) || "io.grpc.StatusException" .equals (name )) {
179+ String msg = String .valueOf (t .getMessage ());
180+ if (msg .contains ("NOT_FOUND" )) {
181+ return true ;
182+ }
183+ }
184+ t = t .getCause ();
164185 }
186+ return false ;
165187 }
166188}
0 commit comments