Tencent/WeKnora · error
unmarshal block: %w
Error message
unmarshal block: %w
What it means
ResolveBlock re-fetches a single block via GET /v1/blocks/{blockID} and unmarshals the JSON body into a notionBlock struct. This error wraps the json.Unmarshal failure, meaning Notion returned a body that is not valid JSON or whose shape does not fit notionBlock (e.g. a non-object payload such as an HTML error page or proxy response). The underlying json error is chained via %w.
Source
Thrown at internal/datasource/connector/notion/client.go:372
return nil, fmt.Errorf("query database %s: not a data_source (%v) and not a database (%v)", id, err, dbErr)
}
if info.DataSourceID == "" {
return nil, fmt.Errorf("database %s has no data sources", id)
}
return c.paginatePages(ctx, http.MethodPost, fmt.Sprintf("/v1/data_sources/%s/query", info.DataSourceID))
}
// ResolveBlock re-fetches a single block to resolve file_upload URLs.
// When a block contains a file_upload type, re-fetching it returns the resolved
// download URL (temporary S3 signed URL, 1-hour expiry).
func (c *notionClient) ResolveBlock(ctx context.Context, blockID string) (*notionBlock, error) {
respBody, err := c.doRequest(ctx, http.MethodGet, "/v1/blocks/"+blockID, nil)
if err != nil {
return nil, err
}
var block notionBlock
if err := json.Unmarshal(respBody, &block); err != nil {
return nil, fmt.Errorf("unmarshal block: %w", err)
}
return &block, nil
}
const maxDownloadSize = 100 * 1024 * 1024 // 100MB — prevent OOM from oversized files
// DownloadFile downloads a file from the given URL (typically an S3 signed URL).
// Does not go through the rate limiter since it's not a Notion API call.
func (c *notionClient) DownloadFile(ctx context.Context, fileURL string) ([]byte, error) {
if err := utils.ValidateURLForSSRF(fileURL); err != nil {
return nil, fmt.Errorf("attachment URL rejected: %w", err)
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fileURL, nil)
if err != nil {
return nil, fmt.Errorf("create download request: %w", err)
}
var lastErr errorView on GitHub (pinned to 988cbb0330)
Solutions
- Log the raw respBody (truncated) alongside the wrapped error to see what Notion actually returned; if it is HTML, look for proxy/auth interception.
- Re-run with a fresh request — a truncated body is often transient; ResolveBlock is already retried by resolveFileUploads paths in some setups.
- Update the notionBlock struct to match the current Notion API version's block schema (check recently changed fields like file_upload).
- Verify the request went to api.notion.com directly, not through an intercepting proxy (check HTTPS_PROXY / corporate gateway).
Example fix
null
Defensive patterns
Strategy: retry
Validate before calling
if len(respBody) == 0 || respBody[0] != '{' {
return fmt.Errorf("unexpected non-JSON block response (%d bytes)", len(respBody))
} Type guard
func isUnmarshalBlockError(err error) bool {
return err != nil && strings.Contains(err.Error(), "unmarshal block:")
} Try / catch
block, err := client.ResolveBlock(ctx, blockID)
if err != nil {
if isUnmarshalBlockError(err) {
// transient/corrupt body: retry once, then skip this block's file resolution
return resolveWithRetry(ctx, blockID, 1)
}
return err
} Prevention
- Log a truncated raw response body on decode failures to distinguish proxies/auth pages from schema drift.
- Keep the notionClient pinned to a Notion API version you have tested the block schema against.
- Check for corporate proxies (HTTPS_PROXY) that can inject HTML into API responses.
When it happens
Trigger: GET /v1/blocks/{blockID} returns 200 with a body that fails to decode into notionBlock: truncated JSON, an HTML interstitial from a proxy/gateway, or a Notion schema field whose type no longer matches the Go struct (e.g. block.type or file upload fields changing type).
Common situations: Corporate proxy or API gateway injecting an error/login page; Notion API schema change after a version bump; corrupted response from a flaky network path where a partial body arrives with a 200 status.
Understand the failure class
Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.
Related errors
- unmarshal page: %w
- unmarshal database: %w
- unmarshal data_sources: %w
- unmarshal data_source: %w
- unmarshal block children response: %w
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/8e01261022e41e60.
Report an issue: GitHub.