alibaba/canal · error · RuntimeException

requestPost remote error, request : {}, statusCode={};{}

Error message

requestPost remote error, request : {}, statusCode={};{}

What it means

Inner throw in HttpHelper.post0() (HttpHelper.java:147-148): fired when the HTTP POST response status is not 200 OK. Builds RuntimeException 'requestPost remote error, request : <url>, statusCode=<statusCode>;<response body>'. As with the GET path, this throw is inside the try block whose catch(Throwable t) at line 150-151 immediately re-wraps it into a NEW RuntimeException ('requestPost remote error, request : <url>', originalException-as-cause). Callers only see the outer message (error 359); the line-147 message is preserved as the cause.

Source

Thrown at instance/manager/src/main/java/com/alibaba/otter/canal/instance/manager/plain/HttpHelper.java:147

            httpPost = new HttpPost(uri);
            StringEntity entity = new StringEntity(requestBody, "UTF-8");
            httpPost.setEntity(entity);
            httpPost.setHeader("Content-Type", "application/json;charset=utf8");
            if (heads != null) {
                for (Map.Entry<String, String> entry : heads.entrySet()) {
                    httpPost.setHeader(entry.getKey(), entry.getValue());
                }
            }

            HttpClientContext context = HttpClientContext.create();
            context.setRequestConfig(config);

            response = httpclient.execute(httpPost, context);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                return EntityUtils.toString(response.getEntity());
            } else {
                throw new RuntimeException("requestPost remote error, request : " + url + ", statusCode=" + statusCode
                                           + ";" + EntityUtils.toString(response.getEntity()));
            }
        } catch (Throwable t) {
            throw new RuntimeException("requestPost remote error, request : " + url, t);
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    // ignore
                }
            }
            if (httpPost != null) {
                httpPost.releaseConnection();
            }
        }
    }

View on GitHub (pinned to 87be50e876)

Solutions

  1. Unwrap getCause() on the RuntimeException reaching the caller — its message contains 'statusCode=...' plus the response body from line 147.
  2. Fix per status: 401/50014 → refresh the manager token; 400 → validate the JSON body being posted; 404 → correct the manager REST path; 500 → inspect manager logs.
  3. Reproduce with curl -X POST -H 'Content-Type: application/json' -d '<body>' <url> using the same headers to see the manager's error response directly.
  4. Ensure the request body is serializable via fastjson2 (post() calls JSON.toJSONString(requestBody)) — a serialization error throws before the request is sent and is caught by the same outer handler.

Example fix

// before — only the outer message is visible
try {
    helper.post(url, heads, payload, timeout);
} catch (RuntimeException e) {
    log.error(e.getMessage()); // 'requestPost remote error, request : <url>'
}

// after — read the cause for statusCode + body
try {
    helper.post(url, heads, payload, timeout);
} catch (RuntimeException e) {
    Throwable cause = e.getCause();
    if (cause != null && cause.getMessage() != null
        && cause.getMessage().contains("statusCode=")) {
        log.error("manager POST rejected: {}", cause.getMessage());
    } else {
        log.error("manager POST failed", e);
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure the payload serializes cleanly before posting (post() calls JSON.toJSONString)
try {
    String json = JSON.toJSONString(requestBody);
    if (StringUtils.isBlank(json)) {
        throw new IllegalArgumentException("requestBody serializes to empty JSON");
    }
} catch (Exception e) {
    throw new IllegalArgumentException("requestBody is not JSON-serializable", e);
}

Try / catch

// The inner throw is re-wrapped; read getCause() for statusCode + response body
try {
    String body = helper.post(url, heads, payload, timeout);
} catch (RuntimeException e) {
    Throwable cause = e.getCause();
    if (cause != null && cause.getMessage() != null && cause.getMessage().contains("statusCode=")) {
        log.error("manager POST rejected: {}", cause.getMessage());
    } else {
        log.error("manager POST failed", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: HttpHelper.post/post0 returning a non-200 status — typically when posting instance config updates/heartbeats to canal-manager and the manager responds with an error code. The response body is concatenated after the statusCode in the message, then re-wrapped by the outer catch.

Common situations: Manager auth token invalid/expired (401/50014); malformed JSON body → 400; manager-side validation failure (500); endpoint not registered (404); conflicting/unchanged config update rejected; rate limiting.

Related errors


AI-assisted analysis of alibaba/canal@87be50e876 (2026-08-14). Data as JSON: /api/errors/b37aa8d053a49739. Report an issue: GitHub.