glanceapp/glance · error

invalid body type, must be either 'json' or 'string'

Error message

invalid body type, must be either 'json' or 'string'

What it means

In the Custom API widget, the .JSON "key" template function marshals a configured option to a JSON string. Unlike FloatOr/StringOr/BoolOr it has no default fallback: if the key is absent from the widget's options map it panics with 'key %q does not exist in options'. Go's template engine recovers the panic and reports it as a template execution error, so the widget render fails with that message.

Source

Thrown at internal/glance/widget-custom-api.go:149

	return defaultValue
}

func (req *CustomAPIRequest) initialize() error {
	if req == nil || req.URL == "" {
		return nil
	}

	if req.Body != nil {
		if req.Method == "" {
			req.Method = http.MethodPost
		}

		if req.BodyType == "" {
			req.BodyType = "json"
		}

		if req.BodyType != "json" && req.BodyType != "string" {
			return errors.New("invalid body type, must be either 'json' or 'string'")
		}

		switch req.BodyType {
		case "json":
			encoded, err := json.Marshal(req.Body)
			if err != nil {
				return fmt.Errorf("marshaling body: %v", err)
			}

			req.bodyReader = bytes.NewReader(encoded)
		case "string":
			bodyAsString, ok := req.Body.(string)
			if !ok {
				return errors.New("body must be a string when body-type is 'string'")
			}

			req.bodyReader = strings.NewReader(bodyAsString)
		}

View on GitHub (pinned to 91324e8de7)

Solutions

  1. Open the Custom API widget's config and add the missing key under options: with any YAML value
  2. Check for typos/case differences between the template's .JSON argument and the options key
  3. If the option is genuinely optional, use .StringOr/.FloatOr/.BoolOr with defaults, or guard in the template
  4. Reload Glance config and re-render the widget to confirm the error clears

Example fix

// before (glance.yml)
- type: custom-api
  template: |
    {{ .JSON "webhook" }}
  options:
    url: http://x
// after: add the referenced key
- type: custom-api
  template: |
    {{ .JSON "webhook" }}
  options:
    url: http://x
    webhook: "https://hooks.example/abc"
Defensive patterns

Strategy: validation

Validate before calling

// template-side guard before calling .JSON
{{ if .Options.HasKey "webhook" }}{{ .JSON "webhook" }}{{ end }}

Try / catch

Go's html/template recovers panics during Execute: capture the returned error and surface its message (which contains 'key %q does not exist in options') to the config author instead of crashing the server. Outside templates, check the map with `value, ok := options[key]; if !ok { ... }` before use.

Prevention

When it happens

Trigger: A template under template or template-partial calls .JSON "foo" but the widget config has no option foo: (renamed key, typo, or the option lives on a different widget instance).

Common situations: Refactoring: the template is updated to reference a new option key but glance.yml is not; copy-pasting a template partial that expects options the target widget does not define; case mismatches (Option vs option).

Related errors


AI-assisted analysis of glanceapp/glance@91324e8de7 (2026-08-15). Data as JSON: /api/errors/a56c03ca9b878573. Report an issue: GitHub.