{"record":{"id":"1ee89bfaab8687de","repo":"Tencent/WeKnora","slug":"unmarshal-database-w","errorCode":null,"errorMessage":"unmarshal database: %w","messagePattern":"unmarshal database: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/datasource/connector/notion/client.go","lineNumber":196,"sourceCode":"}\n\n// databaseInfo holds both the metadata and the data source ID from a single API call.\ntype databaseInfo struct {\n\tPage         notionPage\n\tDataSourceID string\n}\n\n// GetDatabaseInfo retrieves a database container by ID, returning both\n// the page metadata and the primary data source ID in a single API call.\nfunc (c *notionClient) GetDatabaseInfo(ctx context.Context, dbID string) (*databaseInfo, error) {\n\trespBody, err := c.doRequest(ctx, http.MethodGet, \"/v1/databases/\"+dbID, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar db notionPage\n\tif err := json.Unmarshal(respBody, &db); err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshal database: %w\", err)\n\t}\n\tdb.Title = extractTitle(&db)\n\n\tvar dsResult struct {\n\t\tDataSources []struct {\n\t\t\tID string `json:\"id\"`\n\t\t} `json:\"data_sources\"`\n\t}\n\tif err := json.Unmarshal(respBody, &dsResult); err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshal data_sources: %w\", err)\n\t}\n\n\tdsID := \"\"\n\tif len(dsResult.DataSources) > 0 {\n\t\tdsID = dsResult.DataSources[0].ID\n\t}\n\n\treturn &databaseInfo{Page: db, DataSourceID: dsID}, nil","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/datasource/connector/notion/client.go#L178-L214","documentation":"GetDatabaseInfo fetched /v1/databases/{dbID} successfully but the body could not be unmarshaled into notionPage. The database metadata response doesn't match the expected struct shape. Note GetDatabaseInfo also does a second unmarshal for data_sources (error 686); this one covers the main notionPage decode.","triggerScenarios":"GET /v1/databases/{id} returns non-JSON or JSON whose top-level fields conflict with notionPage's typed fields (e.g. title/properties as unexpected types, or a 200 body from a proxy). Called from QueryDatabaseAll and getDatabaseOrDataSourceInfo during sync.","commonSituations":"Notion API version mismatch: older pinned Notion-Version returning database objects with a different schema; ID points to a page (not database) whose response still decodes but here would only fail on shape mismatch; HTML from an intercepting proxy with 200 status.","solutions":["Log the raw response body to see the actual payload shape.","Ensure NotionAPIVersion is consistent with the databases endpoint schema (data_sources fields require 2025-09-03+).","Confirm the ID is a database ID; if unsure use QueryDatabaseAll's fallback path which tries data_source first then resolves via GetDatabaseInfo.","Update notionPage struct tags if the API version was upgraded and fields changed."],"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":"info, err := client.GetDatabaseInfo(ctx, dbID)\nif err != nil && strings.HasPrefix(err.Error(), \"unmarshal database:\") {\n    // inspect raw body / check API version alignment\n    return fmt.Errorf(\"database response shape mismatch for %s: %w\", dbID, err)\n}","preventionTips":["Confirm the ID is a database, not a page (or use QueryDatabaseAll's fallback resolution)","Keep the pinned Notion-Version in sync with the struct expectations","Update fixtures and struct tags after any Notion API upgrade","Log raw responses on unmarshal errors to diagnose quickly"],"tags":["notion","json","unmarshal","database","schema"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}