Skip to content

[Bug] REST API error response only contains top-level message, hiding root cause #10781

@vanasys

Description

@vanasys

[Bug] REST API error response only contains top-level message, hiding root cause

Environment

  • SeaTunnel Version: 2.3.13
  • Deployment Mode: SeaTunnel Engine (Zeta) cluster
  • REST API Endpoint: POST /hazelcast/rest/maps/submit-job

Problem Description

When submitting a job via the REST API and the job fails during initialization, the returned JSON error response only contains the top-level exception message (e.g., Factory initialize failed). The actual root cause — which is usually nested inside the Caused by chain — is completely lost in the HTTP response.

This makes remote debugging extremely difficult because callers of the REST API have no idea what really went wrong (e.g., missing required options, SQL syntax errors, connection timeouts, permission issues).

Steps to Reproduce

  1. Start SeaTunnel cluster.
  2. Submit a deliberately broken job via REST API:
    curl -X POST http://127.0.0.1:5802/hazelcast/rest/maps/submit-job \
      -H "Content-Type: application/json" \
      -d '{
        "env": {"job.mode": "BATCH"},
        "source": [{"FakeSource": {"row.num": 1}}],
        "sink": [{"Doris": {"fenodes": "127.0.0.1:8030"}}]
      }'
  3. Observe the HTTP response:
    {
      "status": "fail",
      "message": "Factory initialize failed"
    }
  4. Compare with the server log logs/seatunnel-engine-server.log, which contains the full Caused by stack trace.

Root Cause Analysis

In seatunnel-starter.jar, the class org.apache.seatunnel.engine.server.rest.filter.ExceptionHandlingFilter handles exceptions thrown during REST request processing.

Currently, in the handleException method, it only calls:

errResponse.setMessage(exception.getMessage());

It does not recursively traverse Throwable.getCause(), so all nested exception information is discarded before being serialized back to the HTTP client.

Expected Behavior

The REST API should return the full error chain so that remote callers can understand the root cause without logging into the server.

Expected response example:

{
  "status": "fail",
  "message": "Factory initialize failed | Caused by: OptionValidationException: the options('database') are required."
}

Suggested Fix

Modify ExceptionHandlingFilter to build a full error message by recursively walking the exception chain:

private String buildFullErrorMessage(Throwable throwable) {
    StringBuilder sb = new StringBuilder();
    sb.append(throwable.getMessage());
    Throwable cause = throwable.getCause();
    while (cause != null) {
        sb.append(" | Caused by: ")
          .append(cause.getClass().getSimpleName())
          .append(": ")
          .append(cause.getMessage());
        cause = cause.getCause();
    }
    return sb.toString().replace("\"", "'");
}

Then use buildFullErrorMessage(exception) instead of exception.getMessage() when constructing the error response.

Additional Context

  • This issue affects all REST API clients that rely on the HTTP response for diagnostics.
  • Server-side logs do contain the full stack trace, but in production environments, REST API users often do not have direct access to the server filesystem.
  • I have verified that patching ExceptionHandlingFilter.class in seatunnel-starter.jar with the above logic resolves the issue locally.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions