fix(slurm): parse structured errors from non-2xx Slurm API responses

Replace ErrorResponse with SlurmAPIError that extracts structured errors/warnings from JSON body when Slurm returns non-2xx (e.g. 404 with valid JSON). Add IsNotFound helper for fallback logic.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
dailz
2026-04-10 13:43:17 +08:00
parent 30f0fbc34b
commit b3d787c97b
3 changed files with 284 additions and 17 deletions

View File

@@ -1,38 +1,85 @@
package slurm
import (
"encoding/json"
"fmt"
"io"
"net/http"
)
// ErrorResponse represents an error returned by the Slurm REST API.
type ErrorResponse struct {
Response *http.Response
Message string
// errorResponseFields is used to parse errors/warnings from a Slurm API error body.
type errorResponseFields struct {
Errors OpenapiErrors `json:"errors,omitempty"`
Warnings OpenapiWarnings `json:"warnings,omitempty"`
}
func (r *ErrorResponse) Error() string {
// SlurmAPIError represents a structured error returned by the Slurm REST API.
// It captures both the HTTP details and the parsed Slurm error array when available.
type SlurmAPIError struct {
Response *http.Response
StatusCode int
Errors OpenapiErrors
Warnings OpenapiWarnings
Message string // raw body fallback when JSON parsing fails
}
func (e *SlurmAPIError) Error() string {
if len(e.Errors) > 0 {
first := e.Errors[0]
detail := ""
if first.Error != nil {
detail = *first.Error
} else if first.Description != nil {
detail = *first.Description
}
if detail != "" {
return fmt.Sprintf("%v %v: %d %s",
e.Response.Request.Method, e.Response.Request.URL,
e.StatusCode, detail)
}
}
return fmt.Sprintf("%v %v: %d %s",
r.Response.Request.Method, r.Response.Request.URL,
r.Response.StatusCode, r.Message)
e.Response.Request.Method, e.Response.Request.URL,
e.StatusCode, e.Message)
}
// IsNotFound reports whether err is a SlurmAPIError with HTTP 404 status.
func IsNotFound(err error) bool {
if apiErr, ok := err.(*SlurmAPIError); ok {
return apiErr.StatusCode == http.StatusNotFound
}
return false
}
// CheckResponse checks the API response for errors. It returns nil if the
// response is a 2xx status code. For non-2xx codes, it reads the response
// body and returns an ErrorResponse.
// body, attempts to parse structured Slurm errors, and returns a SlurmAPIError.
func CheckResponse(r *http.Response) error {
if c := r.StatusCode; c >= 200 && c <= 299 {
return nil
}
errorResponse := &ErrorResponse{Response: r}
data, err := io.ReadAll(r.Body)
if err != nil || len(data) == 0 {
errorResponse.Message = r.Status
return errorResponse
return &SlurmAPIError{
Response: r,
StatusCode: r.StatusCode,
Message: r.Status,
}
}
errorResponse.Message = string(data)
return errorResponse
apiErr := &SlurmAPIError{
Response: r,
StatusCode: r.StatusCode,
Message: string(data),
}
// Try to extract structured errors/warnings from JSON body.
var fields errorResponseFields
if json.Unmarshal(data, &fields) == nil {
apiErr.Errors = fields.Errors
apiErr.Warnings = fields.Warnings
}
return apiErr
}