{"record":{"id":"4831b628b30b1d11","repo":"argoproj/argo-workflows","slug":"http-statustext-statuscode","errorCode":null,"errorMessage":"http.StatusText(statusCode)","messagePattern":"http\\.StatusText\\(statusCode\\)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"server/artifacts/artifact_server.go","lineNumber":665,"sourceCode":"}\n\nfunc (a *ArtifactServer) httpFromError(ctx context.Context, err error, w http.ResponseWriter) {\n\tif err == nil {\n\t\treturn\n\t}\n\tstatusCode := http.StatusInternalServerError\n\te := &apierr.StatusError{}\n\tif errors.As(err, &e) { // check if it's a Kubernetes API error\n\t\t// There is a http error code somewhere in the error stack\n\t\tstatusCode = int(e.Status().Code)\n\t} else {\n\t\t// check if it's an internal ArgoError\n\t\tif argoerr, ok := errors.AsType[argoerrors.ArgoError](err); ok {\n\t\t\tstatusCode = argoerr.HTTPCode()\n\t\t}\n\t}\n\n\thttp.Error(w, http.StatusText(statusCode), statusCode)\n\tif statusCode == http.StatusInternalServerError {\n\t\tlogging.RequireLoggerFromContext(ctx).WithError(err).Error(ctx, \"Artifact Server returned internal error\")\n\t}\n}\n\nfunc (a *ArtifactServer) getArtifactAndDriver(ctx context.Context, nodeID, artifactName string, isInput bool, wf *wfv1.Workflow, fileName *string) (*wfv1.Artifact, common.ArtifactDriver, error) {\n\tlogger := logging.RequireLoggerFromContext(ctx)\n\n\tkubeClient := auth.GetKubeClient(ctx)\n\n\tvar art *wfv1.Artifact\n\n\tnodeStatus, err := wf.Status.Nodes.Get(nodeID)\n\tif err != nil {\n\t\tlogger.WithError(err).WithField(\"nodeID\", nodeID).Error(ctx, \"Was unable to retrieve node\")\n\t\treturn nil, nil, fmt.Errorf(\"was not able to retrieve node\")\n\t}\n\tif isInput {","sourceCodeStart":647,"sourceCodeEnd":683,"githubUrl":"https://github.com/argoproj/argo-workflows/blob/35bff19146f5a6ada77468c431f2624bd577e373/server/artifacts/artifact_server.go#L647-L683","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: bare fmt.Errorf maps to 500\nreturn nil, nil, fmt.Errorf(\"artifact not found: %s\", artifactName)\n// after: coded error maps to 404 via ArgoError.HTTPCode()\nreturn nil, nil, errors.New(errors.CodeNotFound, fmt.Sprintf(\"artifact not found: %s\", artifactName), http.StatusNotFound)","handlingStrategy":"type-guard","validationCode":"// client-side: pre-check the artifact exists before fetching\nwf, err := wfClient.ArgoprojV1alpha1().Workflows(ns).Get(ctx, wfName, metav1.GetOptions{})\nif err != nil { return err }\nnode, ok := wf.Status.Nodes[nodeID]\nif !ok { return fmt.Errorf(\"node %s not found in workflow %s\", nodeID, wfName) }\narts := node.Outputs.Artifacts\nif isInput { arts = node.Inputs.Artifacts }\nfound := false\nfor _, a := range arts { if a.Name == artifactName { found = true } }\nif !found { return fmt.Errorf(\"artifact %s not found on node %s\", artifactName, nodeID)","typeGuard":"func isK8sStatusError(err error) (int, bool) {\n    var e *apierr.StatusError\n    if errors.As(err, &e) && e.Status().Code != 0 {\n        return int(e.Status().Code), true\n    }\n    return 0, false\n}","tryCatchPattern":"// Go: inspect returned error / HTTP status and treat 500 as 'check server logs'\nresp, err := httpClient.Get(artifactURL)\nif err != nil { return err }\nif resp.StatusCode == http.StatusInternalServerError {\n    return fmt.Errorf(\"artifact server returned 500 (cause only in argo-server logs), status=%s\", http.StatusText(500))\n}","preventionTips":["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"],"tags":["http","kubernetes","artifacts","error-mapping"],"backgroundTag":"opaque-500-error-response","analyzedSha":"35bff19146f5a6ada77468c431f2624bd577e373","analyzedAt":"2026-09-03T19:34:35.908Z","contentChangedAt":"2026-09-03T19:34:35.908Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}