conductor-oss/conductor · error · NonTransientException

Unable to upload payload to external storage for workflow: %

Error message

Unable to upload payload to external storage for workflow: %s

What it means

Thrown by ExternalPayloadStorageUtils.verifyAndUpload when uploading a task or workflow payload to external storage fails for any non-transient reason — e.g. storage backend is unreachable with a non-transient error, the upload request is rejected, or serialization fails. TransientException and TerminateWorkflowException are re-thrown as-is; all other exceptions are wrapped in NonTransientException. This occurs when a payload exceeds the threshold size and needs to be offloaded to external storage.

Source

Thrown at core/src/main/java/com/netflix/conductor/core/utils/ExternalPayloadStorageUtils.java:198

                    case WORKFLOW_OUTPUT:
                        externalOutputPayloadStoragePath =
                                uploadHelper(
                                        payloadBytes, payloadSize, PayloadType.WORKFLOW_OUTPUT);
                        ((WorkflowModel) entity)
                                .externalizeOutput(externalOutputPayloadStoragePath);
                        Monitors.recordExternalPayloadStorageUsage(
                                ((WorkflowModel) entity).getWorkflowName(),
                                ExternalPayloadStorage.Operation.WRITE.toString(),
                                PayloadType.WORKFLOW_OUTPUT.toString());
                        break;
                }
            }
        } catch (TransientException | TerminateWorkflowException te) {
            throw te;
        } catch (Exception e) {
            LOGGER.error(
                    "Unable to upload payload to external storage for workflow: {}", workflowId, e);
            throw new NonTransientException(
                    "Unable to upload payload to external storage for workflow: " + workflowId, e);
        }
    }

    @VisibleForTesting
    String uploadHelper(
            byte[] payloadBytes, long payloadSize, ExternalPayloadStorage.PayloadType payloadType) {
        ExternalStorageLocation location =
                externalPayloadStorage.getLocation(
                        ExternalPayloadStorage.Operation.WRITE, payloadType, "", payloadBytes);
        externalPayloadStorage.upload(
                location.getPath(), new ByteArrayInputStream(payloadBytes), payloadSize);
        return location.getPath();
    }

    @VisibleForTesting
    void failTask(TaskModel task, PayloadType payloadType, String errorMsg) {
        LOGGER.error(errorMsg);

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Verify the external storage configuration is correct: bucket name, region, endpoint, and credentials in application.properties.
  2. Ensure the storage credentials have write permissions (s3:PutObject or equivalent) on the target bucket/path.
  3. Check the server log for the underlying cause exception to distinguish between a permissions error, a missing bucket, or a serialization failure.
  4. If the payload contains non-serializable data, clean the task/workflow input or output before the upload threshold is hit.
  5. Verify the external storage implementation bean is correctly wired (e.g. S3ExternalPayloadStorage, not the default no-op).
Defensive patterns

Strategy: validation

Validate before calling

// Verify payload is serializable and within size limits before the upload path
try {
    byte[] bytes = objectMapper.writeValueAsBytes(payload);
    if (bytes.length > maxThresholdBytes) {
        throw new IllegalStateException("Payload exceeds max threshold of " + maxThresholdBytes);
    }
} catch (IOException e) {
    throw new IllegalArgumentException("Payload cannot be serialized", e);
}

Try / catch

try {
    externalPayloadStorageUtils.verifyAndUpload(entity, payloadType);
} catch (NonTransientException e) {
    LOGGER.error("External storage upload failed for workflow — check storage config", e);
    throw e;
} catch (TransientException te) {
    // Retryable — the backend was temporarily unavailable
    throw te;
}

Prevention

When it happens

Trigger: A task or workflow input/output payload exceeds the configured size threshold (triggering external storage upload), but the upload to the external storage backend fails — e.g. S3 returns a 403 (permission denied), 400 (malformed request), the configured bucket doesn't exist, or the ObjectMapper fails to serialize the payload.

Common situations: External storage bucket not created or misconfigured. IAM credentials lack write permissions. Bucket name or region misconfigured in application.properties. Payload contains non-serializable objects causing Jackson to fail during byte array conversion. Storage backend quota exceeded.

Related errors


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/0c9e1ed78b6bd9aa. Report an issue: GitHub.