{"record":{"id":"e82962bdaed7acbd","repo":"Tencent/WeKnora","slug":"unmarshal-data-source-w","errorCode":null,"errorMessage":"unmarshal data_source: %w","messagePattern":"unmarshal data_source: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/datasource/connector/notion/client.go","lineNumber":228,"sourceCode":"\tif len(dsResult.DataSources) > 0 {\n\t\tdsID = dsResult.DataSources[0].ID\n\t}\n\n\treturn &databaseInfo{Page: db, DataSourceID: dsID}, nil\n}\n\n// GetDataSourceInfo retrieves a data source by ID, returning its metadata.\n// In API 2025-09-03+, data_source objects hold the schema/properties and are\n// the target for record queries. The response includes database_parent to\n// locate the database in the workspace hierarchy.\nfunc (c *notionClient) GetDataSourceInfo(ctx context.Context, dsID string) (*notionPage, error) {\n\trespBody, err := c.doRequest(ctx, http.MethodGet, \"/v1/data_sources/\"+dsID, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar ds notionPage\n\tif err := json.Unmarshal(respBody, &ds); err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshal data_source: %w\", err)\n\t}\n\tds.Title = extractTitle(&ds)\n\treturn &ds, nil\n}\n\n// GetBlockChildrenFlat fetches only the direct children of a block (no recursion).\n// Used by discoverPages to quickly scan for child_page/child_database without\n// fetching the full block tree content.\nfunc (c *notionClient) GetBlockChildrenFlat(ctx context.Context, blockID string) ([]notionBlock, error) {\n\tvar allBlocks []notionBlock\n\tvar startCursor string\n\n\tfor {\n\t\tpath := fmt.Sprintf(\"/v1/blocks/%s/children\", blockID)\n\t\tif startCursor != \"\" {\n\t\t\tpath += \"?start_cursor=\" + startCursor\n\t\t}\n","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/datasource/connector/notion/client.go#L210-L246","documentation":"GetDataSourceInfo fetched /v1/data_sources/{dsID} successfully (2xx) but json.Unmarshal into notionPage failed. The data_source object response does not match the notionPage struct. Since data_source objects are a newer API concept (2025-09-03+), this is the most version-sensitive unmarshal in the client.","triggerScenarios":"GET /v1/data_sources/{id} returning JSON whose shape breaks notionPage decoding — typically after a Notion API version bump changing data_source fields, or a 200 non-JSON body from an intercepting proxy. Called from getDatabaseOrDataSourceInfo during discovery.","commonSituations":"API contract drift: upgrading Notion-Version changes data_source schema (e.g. properties moved/renamed) while notionPage expects old field types; test fixtures outdated; proxy returning 200 HTML error pages.","solutions":["Log the raw response body and compare field-by-field against notionPage's struct tags.","Align NotionAPIVersion with the schema notionPage was written for; update struct tags after version upgrades.","Verify the dsID is a genuine data_source ID (from databases/{id}.data_sources or search), not a page/database ID.","Consider decoding into a lenient structure (json.RawMessage for volatile fields) to be resilient to additive API changes."],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isLikelyJSON(b []byte) bool {\n    t := bytes.TrimSpace(b)\n    return len(t) > 0 && (t[0] == '{' || t[0] == '[')\n}","tryCatchPattern":"ds, err := client.GetDataSourceInfo(ctx, dsID)\nif err != nil && strings.HasPrefix(err.Error(), \"unmarshal data_source:\") {\n    // API version drift is the usual cause; log body and pin matching version\n    return fmt.Errorf(\"data_source schema mismatch (%s): %w\", dsID, err)\n}","preventionTips":["Pin Notion-Version to the version the structs were written against","Only pass real data_source IDs (from data_sources[] or search) into this call","Use json.RawMessage for volatile fields to absorb additive schema changes","Re-run integration tests against the live API after every version bump"],"tags":["notion","json","unmarshal","data-source","schema"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}