windmill-labs/windmill · error

string(res.Body)

Error message

string(res.Body)

What it means

GetVariable fetches a Windmill variable's decrypted value. When the HTTP response status is not 2xx, the go client surfaces the raw response body (string(res.Body)) as the error message. The text you see is whatever the Windmill API returned — e.g. a JSON error body describing 404 variable not found or 403 forbidden.

Source

Thrown at go-client/windmill.go:51

		Client:    client,
		Workspace: workspace,
	}, nil
}
func newBool(b bool) *bool {
	return &b
}

func GetVariable(path string) (string, error) {
	client, err := GetClient()
	if err != nil {
		return "", err
	}
	res, err := client.Client.GetVariableValueWithResponse(context.Background(), client.Workspace, path, &api.GetVariableValueParams{})
	if err != nil {
		return "", err
	}
	if res.StatusCode()/100 != 2 {
		return "", errors.New(string(res.Body))
	}
	return *res.JSON200, nil
}

func GetResource(path string) (interface{}, error) {
	client, err := GetClient()
	if err != nil {
		return nil, err
	}
	params := api.GetResourceValueInterpolatedParams{}
	res, err := client.Client.GetResourceValueInterpolatedWithResponse(context.Background(), client.Workspace, path, &params)
	if err != nil {
		return nil, err
	}
	if res.StatusCode()/100 != 2 {
		return nil, errors.New(string(res.Body))
	}
	return *res.JSON200, nil

View on GitHub (pinned to e474e8803c)

Solutions

  1. Read the returned body (it contains the API's JSON error, e.g. 404 'Variable not found') and fix the path accordingly
  2. Verify the workspace in GetClient() matches where the variable lives
  3. Check that the API token/worker context has permission to read that variable
  4. Use the Windmill UI or `wmill variable list` to confirm the exact path

Example fix

// before
v, _ := windmill.GetVariable(context.Background(), "secrets/api_key")
// after
v, err := windmill.GetVariable(context.Background(), "secrets/api_key")
if err != nil {
    log.Fatalf("get variable: %v", err) // body reveals 404/403 detail
}
Defensive patterns

Strategy: try-catch

Validate before calling

// validate the variable path format before calling
if path == "" || !strings.HasPrefix(path, "/") && !strings.HasPrefix(path, "u/") && !strings.HasPrefix(path, "f/") {
    return fmt.Errorf("invalid variable path: %q", path)
}

Type guard

func isNotFoundVarErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "not found")
}

Try / catch

v, err := windmill.GetVariable(ctx, path)
if err != nil {
    if strings.Contains(err.Error(), "404") || strings.Contains(err.Error(), "not found") {
        // handle missing variable (create it, use default)
    } else if strings.Contains(err.Error(), "403") || strings.Contains(err.Error(), "permission") {
        // handle access denied
    }
    return err
}

Prevention

When it happens

Trigger: windmill.GetVariable(ctx, path) hitting the API and receiving a non-2xx status: variable does not exist at the given path, the workspace is wrong, or the caller's token lacks read permission on the variable.

Common situations: Typo in the variable path; running locally with a token scoped to a different workspace; variables requiring elevated permissions the token doesn't have; deleted/renamed variables after refactoring.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/612d109e26586d61. Report an issue: GitHub.