{"record":{"id":"9b06151273fed2f3","repo":"Tencent/WeKnora","slug":"unmarshal-page-results-w","errorCode":null,"errorMessage":"unmarshal page results: %w","messagePattern":"unmarshal page results: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/datasource/connector/notion/client.go","lineNumber":467,"sourceCode":"\t\t} else {\n\t\t\tp := path\n\t\t\tif startCursor != \"\" {\n\t\t\t\tp += \"?start_cursor=\" + startCursor + \"&page_size=100\"\n\t\t\t}\n\t\t\trespBody, err = c.doRequest(ctx, method, p, nil)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"paginate %s: %w\", path, err)\n\t\t}\n\n\t\tvar resp paginatedResponse\n\t\tif err := json.Unmarshal(respBody, &resp); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unmarshal paginated response: %w\", err)\n\t\t}\n\n\t\tvar pages []notionPage\n\t\tif err := json.Unmarshal(resp.Results, &pages); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unmarshal page results: %w\", err)\n\t\t}\n\n\t\tfor i := range pages {\n\t\t\tpages[i].Title = extractTitle(&pages[i])\n\t\t}\n\n\t\tallPages = append(allPages, pages...)\n\n\t\tif !resp.HasMore || resp.NextCursor == \"\" {\n\t\t\tbreak\n\t\t}\n\t\tstartCursor = resp.NextCursor\n\t}\n\n\treturn allPages, nil\n}\n\n// --- Title extraction helpers ---","sourceCodeStart":449,"sourceCodeEnd":485,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/datasource/connector/notion/client.go#L449-L485","documentation":"paginatePages unmarshals resp.Results (a json.RawMessage) into []notionPage. If individual page/database objects don't match the notionPage struct, unmarshal fails with this wrapper. Notion returns polymorphic objects (page vs database vs child_page), so schema drift breaks decoding.","triggerScenarios":"SearchPages or QueryDatabaseAll receives results whose objects contain unexpected field types (e.g. properties with shapes the notionPage struct doesn't model) or null where a struct is expected.","commonSituations":"Notion API adds/changes object types returned by the search endpoint; a workspace contains object types (comments, child_database variants) not covered by the struct definitions.","solutions":["Inspect the wrapped json error — it names the offending field and type","Compare the returned JSON against the notionPage struct in types.go","Make strict fields pointers or use json.RawMessage for polymorphic sections","Update connector types to match the current Notion API schema"],"exampleFix":"// before\nvar pages []notionPage\nif err := json.Unmarshal(resp.Results, &pages); err != nil {\n    return nil, fmt.Errorf(\"unmarshal page results: %w\", err)\n}\n// after\nvar raw []json.RawMessage\njson.Unmarshal(resp.Results, &raw)\nfor _, r := range raw {\n    var p notionPage\n    if err := json.Unmarshal(r, &p); err != nil {\n        continue // skip objects that don't fit the schema\n    }\n    pages = append(pages, p)\n}","handlingStrategy":"type-guard","validationCode":"var probe struct { Results []json.RawMessage `json:\"results\"` }; if json.Unmarshal(body, &probe) != nil { return errors.New(\"unexpected notion response shape\") }","typeGuard":"func isValidNotionPage(raw json.RawMessage) bool {\n    var probe struct{ ID string `json:\"id\"` }\n    return json.Unmarshal(raw, &probe) == nil && probe.ID != \"\"\n}","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"unmarshal page results\") {\n    log.Printf(\"notion page schema mismatch: %v\", err)\n    return partialOrEmptyResult // degrade instead of failing sync\n}","preventionTips":["Keep notionPage struct fields optional/pointers where Notion allows null","Test the connector against workspaces with all object types (pages, databases)","Pin and track the Notion API version for schema changes"],"tags":["notion","json","schema","unmarshal"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}