argoproj/argo-workflows · error
http.StatusText(statusCode)
Error message
http.StatusText(statusCode)
What it means
This is not a distinct error message but the HTTP status text written to the response body by ArtifactServer.httpFromError (server/artifacts/artifact_server.go:665). The handler maps an error returned by artifact lookup/streaming into an HTTP status: a Kubernetes apierr.StatusError supplies its own code, an internal argoerrors.ArgoError supplies HTTPCode(), and anything else falls back to 500 'Internal Server Error'. The response body therefore contains only a bare status phrase like 'Internal Server Error' with no underlying detail, which makes debugging hard.
Source
Thrown at server/artifacts/artifact_server.go:665
}
func (a *ArtifactServer) httpFromError(ctx context.Context, err error, w http.ResponseWriter) {
if err == nil {
return
}
statusCode := http.StatusInternalServerError
e := &apierr.StatusError{}
if errors.As(err, &e) { // check if it's a Kubernetes API error
// There is a http error code somewhere in the error stack
statusCode = int(e.Status().Code)
} else {
// check if it's an internal ArgoError
if argoerr, ok := errors.AsType[argoerrors.ArgoError](err); ok {
statusCode = argoerr.HTTPCode()
}
}
http.Error(w, http.StatusText(statusCode), statusCode)
if statusCode == http.StatusInternalServerError {
logging.RequireLoggerFromContext(ctx).WithError(err).Error(ctx, "Artifact Server returned internal error")
}
}
func (a *ArtifactServer) getArtifactAndDriver(ctx context.Context, nodeID, artifactName string, isInput bool, wf *wfv1.Workflow, fileName *string) (*wfv1.Artifact, common.ArtifactDriver, error) {
logger := logging.RequireLoggerFromContext(ctx)
kubeClient := auth.GetKubeClient(ctx)
var art *wfv1.Artifact
nodeStatus, err := wf.Status.Nodes.Get(nodeID)
if err != nil {
logger.WithError(err).WithField("nodeID", nodeID).Error(ctx, "Was unable to retrieve node")
return nil, nil, fmt.Errorf("was not able to retrieve node")
}
if isInput {View on GitHub (pinned to 35bff19146)
Solutions
- Read the argo-server logs, not the response body — the underlying error is logged ('Artifact Server returned internal error') only when the mapped status is 500
- Verify the artifact name and node ID exist on the workflow (`argo get <wf> -o yaml` and inspect status.nodes[].outputs.artifacts)
- Check artifact repository configuration (artifactRepository in workflow-controller-configmap or spec.artifactRepositoryRef) and the driver credentials/secret
- If you control the code path, wrap errors with errors.New(code, msg, http.StatusNotFound) so httpFromError maps them to meaningful status codes
Example fix
// before: bare fmt.Errorf maps to 500
return nil, nil, fmt.Errorf("artifact not found: %s", artifactName)
// after: coded error maps to 404 via ArgoError.HTTPCode()
return nil, nil, errors.New(errors.CodeNotFound, fmt.Sprintf("artifact not found: %s", artifactName), http.StatusNotFound) Defensive patterns
Strategy: type-guard
Validate before calling
// client-side: pre-check the artifact exists before fetching
wf, err := wfClient.ArgoprojV1alpha1().Workflows(ns).Get(ctx, wfName, metav1.GetOptions{})
if err != nil { return err }
node, ok := wf.Status.Nodes[nodeID]
if !ok { return fmt.Errorf("node %s not found in workflow %s", nodeID, wfName) }
arts := node.Outputs.Artifacts
if isInput { arts = node.Inputs.Artifacts }
found := false
for _, a := range arts { if a.Name == artifactName { found = true } }
if !found { return fmt.Errorf("artifact %s not found on node %s", artifactName, nodeID) Type guard
func isK8sStatusError(err error) (int, bool) {
var e *apierr.StatusError
if errors.As(err, &e) && e.Status().Code != 0 {
return int(e.Status().Code), true
}
return 0, false
} Try / catch
// Go: inspect returned error / HTTP status and treat 500 as 'check server logs'
resp, err := httpClient.Get(artifactURL)
if err != nil { return err }
if resp.StatusCode == http.StatusInternalServerError {
return fmt.Errorf("artifact server returned 500 (cause only in argo-server logs), status=%s", http.StatusText(500))
} Prevention
- Always validate artifact name and node ID against the live Workflow object before calling the artifact download API
- Map expected conditions (missing artifact, RBAC) to coded errors server-side so clients get 404/403 instead of opaque 500s
- Monitor argo-server logs for 'Artifact Server returned internal error' to catch repository misconfigurations early
- Check artifact-repository config and credentials after upgrades or cluster migrations
When it happens
Trigger: Any GET via GetArtifactFile, getArtifact, or getArtifactByUID whose underlying call fails with an error that is neither a Kubernetes StatusError nor an ArgoError: e.g. 'artifact not found: ...' from getArtifactAndDriver (plain fmt.Errorf, maps to 500), storage-driver failures from S3/GCS/Azure, or RBAC failures on the workflow Get call.
Common situations: Client downloads an artifact for a wrong/typo'd artifact name or node ID and gets a bare 500 instead of 404; artifact repository credentials misconfigured so the driver fails; archived workflow accessed by UID when the archive DB lacks the record; upgrading Argo where error types changed and no longer implement ArgoError.
Related errors
- Artifact driver validation failed: The following artifact dr
- failed to stream artifact: %v
- failed to get and store artifact data: %w
- failed to re-enter working directory %q after staging input
- failed to stat input artifact %q at %s: %w
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/4831b628b30b1d11.
Report an issue: GitHub.