Repository: kestra-io/kestra (java)

Issue:
Skip error message display on Cancelled executions e.g. due to EXECUTION_CONDITION SLA with CANCEL behavior
### Feature description

![image](https://github.com/user-attachments/assets/d90199f4-1e33-44af-9796-0bbdfb67678c)

Repro - run with CANCEL input:

```yaml
id: sla_demo
namespace: company.team

inputs:
  - id: behavior
    type: SELECT
    values:
      - FAIL
      - CANCEL
      - NONE
    defaults: NONE

sla:
  - id: maxDuration
    type: MAX_DURATION
    behavior: FAIL
    duration: PT2S
    labels:
      sla: missed

  - id: cancel
    type: EXECUTION_CONDITION
    behavior: CANCEL # Execution killed due to missed SLA
    condition: "{{ inputs.behavior == 'CANCEL' }}"
    labels:
      sla: missed
      
  - id: pass_and_log
    type: EXECUTION_CONDITION
    behavior: NONE # SLA passed with a log message
    condition: "{{ inputs.behavior == 'NONE' }}"
    labels:
      sla: passed

tasks:
  - id: hi
    type: io.kestra.plugin.core.log.Log
    message: Welcome!

  - id: sleep
    type: io.kestra.plugin.core.flow.Sleep
    duration: PT3S

  - id: bye
    type: io.kestra.plugin.core.log.Log
    message: Goodbye!
```

Pull request:
fix(ui): only display exception error for FAILED exceptions
Fixes #6098

Patch:
diff --git a/core/src/main/java/io/kestra/core/runners/Worker.java b/core/src/main/java/io/kestra/core/runners/Worker.java
index 11f99e945..7e8598cae 100644
--- a/core/src/main/java/io/kestra/core/runners/Worker.java
+++ b/core/src/main/java/io/kestra/core/runners/Worker.java
@@ -61,6 +61,8 @@ import static io.kestra.core.utils.Rethrow.throwFunction;
 @Slf4j
 @Introspected
 public class Worker implements Service, Runnable, AutoCloseable {
+    public static final String EXECUTOR_NAME = "worker";
+
     private static final ObjectMapper MAPPER = JacksonMapper.ofJson();
     private static final String SERVICE_PROPS_WORKER_GROUP = "worker.group";
 
@@ -158,7 +160,7 @@ public class Worker implements Service, Runnable, AutoCloseable {
         this.numThreads = numThreads;
         this.workerGroup = workerGroupService.resolveGroupFromKey(workerGroupKey);
         this.eventPublisher = eventPublisher;
-        this.executorService = executorsUtils.maxCachedThreadPool(numThreads, "worker");
+        this.executorService = executorsUtils.maxCachedThreadPool(numThreads, EXECUTOR_NAME);
         this.setState(ServiceState.CREATED);
     }
 
diff --git a/core/src/main/java/io/kestra/core/runners/pebble/PebbleUtils.java b/core/src/main/java/io/kestra/core/runners/pebble/PebbleUtils.java
new file mode 100644
index 000000000..395806725
--- /dev/null
+++ b/core/src/main/java/io/kestra/core/runners/pebble/PebbleUtils.java
@@ -0,0 +1,24 @@
+package io.kestra.core.runners.pebble;
+
+import io.kestra.core.runners.Worker;
+import io.micronaut.context.annotation.Value;
+import jakarta.inject.Singleton;
+
+@Singleton
+public class PebbleUtils {
+    @Value("${kestra.server-type:}") // default to empty as tests didn't set this property
+    private String serverType;
+
+    public boolean calledOnWorker() {
+        if ("WORKER".equals(serverType)) {
+            return true;
+        }
+        if ("STANDALONE".equals(serverType)) {
+            // check that it's called inside a worker thread
+            // Note: this is not ideal as we check that it starts with the name used in the Worker executor
+            return Thread.currentThread().getName().startsWith(Worker.EXECUTOR_NAME + "_");
+        }
+
+        return false;
+    }
+}
diff --git a/core/src/main/java/io/kestra/core/runners/pebble/functions/ErrorLogsFunction.java b/core/src/main/java/io/kestra/core/runners/pebble/functions/ErrorLogsFunction.java
index e83f9fdc7..2090cf9f0 100644
--- a/core/src/main/java/io/kestra/core/runners/pebble/functions/ErrorLogsFunction.java
+++ b/core/src/main/java/io/kestra/core/runners/pebble/functions/ErrorLogsFunction.java
@@ -1,14 +1,20 @@
 package io.kestra.core.runners.pebble.functions;
 
+import io.kestra.core.models.executions.LogEntry;
+import io.kestra.core.models.tasks.retrys.Exponential;
+import io.kestra.core.runners.pebble.PebbleUtils;
 import io.kestra.core.services.LogService;
+import io.kestra.core.utils.ListUtils;
+import io.kestra.core.utils.RetryUtils;
 import io.micronaut.context.annotation.Requires;
-import io.micronaut.core.util.StringUtils;
+import io.pebbletemplates.pebble.error.PebbleException;
 import io.pebbletemplates.pebble.extension.Function;
 import io.pebbletemplates.pebble.template.EvaluationContext;
 import io.pebbletemplates.pebble.template.PebbleTemplate;
 import jakarta.inject.Inject;
 import jakarta.inject.Singleton;
 
+import java.time.Duration;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -19,6 +25,12 @@ public class ErrorLogsFunction  implements Function {
     @Inject
     private LogService logService;
 
+    @Inject
+    private PebbleUtils pebbleUtils;
+
+    @Inject
+    private RetryUtils retryUtils;
+
     @Override
     public List<String> getArgumentNames() {
         return Collections.emptyList();
@@ -27,8 +39,27 @@ public class ErrorLogsFunction  implements Function {
     @Override
     @SuppressWarnings("unchecked")
     public Object execute(Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
+        if (!pebbleUtils.calledOnWorker()) {
+            throw new PebbleException(null, "The 'errorLogs' function can only be used in the Worker as it access logs from the database.", lineNumber, self.getName());
+        }
+
         Map<String, String> flow = (Map<String, String>) context.getVariable("flow");
         Map<String, String> execution = (Map<String, String>) context.getVariable("execution");
-        return logService.errorLogs(flow.get("tenantId"), execution.get("id"));
+
+        RetryUtils.Instance<List<LogEntry>, Throwable> retry = retryUtils.of(Exponential.builder()
+            .delayFactor(2.0)
+            .interval(Duration.ofMillis(100))
+            .maxInterval(Duration.ofSeconds(1))
+            .maxAttempt(-1)
+            .maxDuration(Duration.ofSeconds(5))
+            .build());
+
+        try {
+            return retry.run( logs -> ListUtils.isEmpty(logs), () -> logService.errorLogs(flow.get("tenantId"), execution.get("id")));
+        } catch (RetryUtils.RetryFailed e) {
+            return Collections.emptyList();
+        } catch (Throwable e) {
+            throw new PebbleException(e, "Unable to fetch error logs");
+        }
     }
 }
diff --git a/core/src/main/java/io/kestra/core/runners/pebble/functions/ReadFileFunction.java b/core/src/main/java/io/kestra/core/runners/pebble/functions/ReadFileFunction.java
index 90c3ca40b..ae3f24e11 100644
--- a/core/src/main/java/io/kestra/core/runners/pebble/functions/ReadFileFunction.java
+++ b/core/src/main/java/io/kestra/core/runners/pebble/functions/ReadFileFunction.java
@@ -67,17 +67,6 @@ public class ReadFileFunction implements Function {
         }
     }
 
-    private boolean calledOnWorker() {
-        if ("WORKER".equals(serverType)) {
-            return true;
-        }
-        if ("STANDALONE".equals(serverType)) {
-            // check that it's called inside a worker thread
-            return Thread.currentThread().getClass().getName().equals("io.kestra.core.runners.Worker$WorkerThread");
-        }
-        return false;
-    }
-
     @SuppressWarnings("unchecked")
     private String readFromNamespaceFile(EvaluationContext context, String path) throws IOException {
         Map<String, String> flow = (Map<String, String>) context.getVariable("flow");
diff --git a/ui/src/components/executions/Overview.vue b/ui/src/components/executions/Overview.vue
index 8cc4aecd0..0a3b70d64 100644
--- a/ui/src/components/executions/Overview.vue
+++ b/ui/src/components/executions/Overview.vue
@@ -198,7 +198,7 @@
                 }
             },
             isFailed() {
-                return State.isFailed(this.execution.state.current);
+                return this.execution.state.current === State.FAILED;
             },
             load() {
                 this.$store


Test patch:
diff --git a/core/src/test/java/io/kestra/core/runners/pebble/functions/ErrorLogsFunctionTest.java b/core/src/test/java/io/kestra/core/runners/pebble/functions/ErrorLogsFunctionTest.java
index 23e1d8790..c1ecddf2e 100644
--- a/core/src/test/java/io/kestra/core/runners/pebble/functions/ErrorLogsFunctionTest.java
+++ b/core/src/test/java/io/kestra/core/runners/pebble/functions/ErrorLogsFunctionTest.java
@@ -5,6 +5,7 @@ import io.kestra.core.junit.annotations.KestraTest;
 import io.kestra.core.models.executions.LogEntry;
 import io.kestra.core.repositories.LogRepositoryInterface;
 import io.kestra.core.runners.VariableRenderer;
+import io.micronaut.context.annotation.Property;
 import jakarta.inject.Inject;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
@@ -18,6 +19,7 @@ import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.is;
 
 @KestraTest
+@Property(name = "kestra.server-type", value = "WORKER")
 class ErrorLogsFunctionTest {
     @Inject
     private LogRepositoryInterface logRepository;