argoproj/argo-workflows · error
%s: %s
Error message
%s: %s
What it means
A plugin HTTP call received a response with an unexpected status code (not 2xx and not the handled transient status). The plugin executor surfaces the HTTP status line plus the raw response body as the error, e.g. '500 Internal Server Error: <body>'. It tells you the plugin's own HTTP endpoint rejected the request.
Source
Thrown at workflow/util/plugin/plugin.go:93
case http.StatusOK:
return json.NewDecoder(resp.Body).Decode(reply)
case http.StatusNotFound:
log.Info(ctx, "method not found, not calling again")
p.invalid[method] = true
_, err := io.Copy(io.Discard, resp.Body)
return err
case http.StatusServiceUnavailable:
data, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
return errors.NewErrTransient(string(data))
default:
data, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
return fmt.Errorf("%s: %s", resp.Status, string(data))
}
})
}
View on GitHub (pinned to 35bff19146)
Solutions
- Read the body in the error message — it usually contains the plugin's own error explaining the failure
- Verify the plugin endpoint URL/port in the plugin ConfigMap matches the running plugin service
- Check the plugin's logs for the corresponding request failure and fix server-side (auth, crash, bad payload)
- If the status indicates a transient condition in your plugin, return the recognized transient status so Argo retries
Example fix
// before (plugin handler returns bare 500) w.WriteHeader(500) // after (actionable body) http.Error(w, "executor plugin: failed to load template: missing arg", 500)
Defensive patterns
Strategy: retry
Validate before calling
// preflight: check the plugin endpoint is reachable
resp, err := http.Get(pluginURL + "/health")
if err != nil || resp.StatusCode != 200 {
return fmt.Errorf("plugin endpoint %s unhealthy", pluginURL)
} Try / catch
err := executor.CallPlugin(ctx, ...)
if err != nil {
var statusErr interface{ HTTPStatus() int }
if strings.Contains(err.Error(), ": ") && !util_errors.IsTransientErr(ctx, err) {
// non-transient HTTP failure: check plugin logs, do not retry blindly
}
} Prevention
- Monitor plugin service health and logs alongside the workflow controller
- Pin plugin versions and verify endpoint URLs after config changes
- Return recognizable transient statuses from the plugin for retryable conditions
When it happens
Trigger: workflow/executor/plugin driver posts a request to a plugin's HTTP endpoint and the response status falls into the default switch case — any non-handled status (e.g. 400, 403, 404, 500) — and the status+body are wrapped via fmt.Errorf("%s: %s", resp.Status, data).
Common situations: Plugin service not deployed/misconfigured (404), plugin auth failing (401/403), plugin crashing on the request (500), wrong port/URL in the plugin config; non-2xx statuses other than the recognized transient one.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- failed to stream artifact: %v
- CodeNotFound
- failed to get and store artifact data: %w
- request failed with: %w
- plugin %s is directory check failed: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/b05a3f9c9b1c4044.
Report an issue: GitHub.