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

  1. 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
  2. Verify the artifact name and node ID exist on the workflow (`argo get <wf> -o yaml` and inspect status.nodes[].outputs.artifacts)
  3. Check artifact repository configuration (artifactRepository in workflow-controller-configmap or spec.artifactRepositoryRef) and the driver credentials/secret
  4. 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

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


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/4831b628b30b1d11. Report an issue: GitHub.