glanceapp/glance · error

invalid response JSON

Error message

invalid response JSON

What it means

Custom API widget templates call .Subrequest "key" to get data pre-fetched by a subrequests entry. If the key is not defined in the widget's subrequests map, there is no safe zero value to return, so the code panics with 'subrequest with key %q has not been defined'; the template engine converts the panic into an execution error for that render.

Source

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

	}
	defer resp.Body.Close()

	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	body := strings.TrimSpace(string(bodyBytes))

	if !req.SkipJSONValidation && body != "" && !gjson.Valid(body) {
		if 200 <= resp.StatusCode && resp.StatusCode < 300 {
			truncatedBody, isTruncated := limitStringLength(body, 100)
			if isTruncated {
				truncatedBody += "... <truncated>"
			}

			slog.Error("Invalid response JSON in custom API widget", "url", req.httpRequest.URL.String(), "body", truncatedBody)
			return nil, errors.New("invalid response JSON")
		}

		return nil, fmt.Errorf("%d %s", resp.StatusCode, http.StatusText(resp.StatusCode))

	}

	return &customAPIResponseData{
		JSON:     decoratedGJSONResult{gjson.Parse(body)},
		Response: resp,
	}, nil
}

func fetchAndRenderCustomAPIRequest(
	primaryReq *CustomAPIRequest,
	subReqs map[string]*CustomAPIRequest,
	options customAPIOptions,
	tmpl *template.Template,
) (template.HTML, error) {

View on GitHub (pinned to 91324e8de7)

Solutions

  1. Add a subrequests entry with the exact key the template passes to .Subrequest
  2. Fix typos/case mismatches between template argument and subrequests key
  3. Keep shared partials self-consistent: any widget including the partial must define every subrequest it uses

Example fix

// before (glance.yml)
- type: custom-api
  template: |
    {{ (.Subrequest "stats").JSON.Get "uptime" }}
// after: define the referenced subrequest
- type: custom-api
  template: |
    {{ (.Subrequest "stats").JSON.Get "uptime" }}
  subrequests:
    stats:
      url: http://host/api/stats
Defensive patterns

Strategy: validation

Validate before calling

// before rendering, verify every key the template uses exists
for _, key := range templateSubrequestKeys {
    if _, ok := subrequests[key]; !ok {
        return fmt.Errorf("subrequest %q missing from config", key)
    }
}

Try / catch

Rely on template recovery: html/template turns the panic into an execution error containing 'subrequest with key %q has not been defined'; log that error per-widget so one bad widget does not take down the page.

Prevention

When it happens

Trigger: Template references .Subrequest "stats" but the widget config has no subrequests: stats: entry (typo, renamed key, or the subrequest was removed while the template still references it). Also occurs when a shared partial expects a subrequest the including widget never declared.

Common situations: Reusing a template across several Custom API widgets where only some define the subrequest; renaming a subrequest key in glance.yml without updating the template; case-sensitivity mismatches.

Related errors


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