gastownhall/beads · error

parse data source query response: %w

Error message

parse data source query response: %w

What it means

QueryDataSource POSTs to /data_sources/{id}/query and json.Unmarshals the response body into QueryDataSourceResponse. This error wraps any unmarshal failure, meaning the Notion API returned a 200 body that does not match the expected pagination envelope (has_more, next_cursor, results). It indicates a response-shape mismatch rather than an HTTP failure.

Source

Thrown at internal/notion/client.go:138

func (c *Client) QueryDataSource(ctx context.Context, dataSourceID string) ([]Page, error) {
	var pages []Page
	var cursor string
	for pageNum := 0; pageNum < maxQueryPages; pageNum++ {
		request := map[string]interface{}{
			"page_size":   maxPageSize,
			"result_type": "page",
		}
		if cursor != "" {
			request["start_cursor"] = cursor
		}

		body, err := c.doRequest(ctx, http.MethodPost, "/data_sources/"+url.PathEscape(dataSourceID)+"/query", request)
		if err != nil {
			return nil, err
		}
		var resp QueryDataSourceResponse
		if err := json.Unmarshal(body, &resp); err != nil {
			return nil, fmt.Errorf("parse data source query response: %w", err)
		}
		pages = append(pages, resp.Results...)
		if !resp.HasMore || resp.NextCursor == "" {
			return pages, nil
		}
		cursor = resp.NextCursor
	}
	return nil, fmt.Errorf("query pagination exceeded %d pages", maxQueryPages)
}

func (c *Client) CreatePage(ctx context.Context, dataSourceID string, properties map[string]interface{}) (*Page, error) {
	request := map[string]interface{}{
		"parent": map[string]interface{}{
			"type":           "data_source_id",
			"data_source_id": dataSourceID,
		},
		"properties": properties,
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Log the raw response body (resp body from doRequest) to see what actually came back before the unmarshal failed.
  2. Pin/verify the Notion-Version header matches the API version the structs were written for.
  3. Check for a proxy or gateway rewriting responses (content-type should be application/json).
  4. Update the notion client package so QueryDataSourceResponse matches the current Notion API schema.
  5. Retry once on transient mismatch if the body looks like a gateway error page.

Example fix

// before
var resp QueryDataSourceResponse
if err := json.Unmarshal(body, &resp); err != nil {
    return nil, fmt.Errorf("parse data source query response: %w", err)
}
// after
var resp QueryDataSourceResponse
if err := json.Unmarshal(body, &resp); err != nil {
    return nil, fmt.Errorf("parse data source query response: %w (body: %.200s)", err, body)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if !strings.Contains(httpRes.Header.Get("Content-Type"), "application/json") {
    return fmt.Errorf("expected JSON response, got %q", httpRes.Header.Get("Content-Type"))
}

Try / catch

pages, err := client.QueryDataSource(ctx, dsID, query)
if err != nil {
    if strings.Contains(err.Error(), "parse data source query response") {
        // log raw body / flag API-shape drift, retry once
    }
    return err
}

Prevention

When it happens

Trigger: Calling Client.QueryDataSource when the Notion API returns JSON that cannot unmarshal into QueryDataSourceResponse — e.g. an API version change renaming fields, a proxy/gateway returning HTML or an error page with 200 status, or types/properties fields in results not matching the Page/DataSource struct tags.

Common situations: Notion API version upgrades changing field names or types; corporate proxies or WAFs injecting non-JSON bodies; overly strict Go struct tags in the notion package after a library update; intermittent gateway errors returning HTML error pages.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/5380ee777d0c4923. Report an issue: GitHub.