{"record":{"id":"cb4fbe2540f06ca4","repo":"Tencent/WeKnora","slug":"query-database-s-not-a-data-source-v-and-not","errorCode":null,"errorMessage":"query database %s: not a data_source (%v) and not a database (%v)","messagePattern":"query database (.+?): not a data_source \\((.+?)\\) and not a database \\((.+?)\\)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/datasource/connector/notion/client.go","lineNumber":354,"sourceCode":"\t}\n\n\treturn allBlocks, nil\n}\n\n// QueryDatabaseAll retrieves all records from a database via POST /v1/data_sources/{id}/query.\n// Accepts either a data_source_id (from search) or a database_id (from child_database blocks).\n// For database_ids, resolves to data_source_id via GET /v1/databases/{id}.\nfunc (c *notionClient) QueryDatabaseAll(ctx context.Context, id string) ([]notionPage, error) {\n\t// Try as data_source_id directly\n\trecords, err := c.paginatePages(ctx, http.MethodPost, fmt.Sprintf(\"/v1/data_sources/%s/query\", id))\n\tif err == nil {\n\t\treturn records, nil\n\t}\n\n\t// If 404, id might be a database container ID — resolve to data_source_id\n\tinfo, dbErr := c.GetDatabaseInfo(ctx, id)\n\tif dbErr != nil {\n\t\treturn nil, fmt.Errorf(\"query database %s: not a data_source (%v) and not a database (%v)\", id, err, dbErr)\n\t}\n\tif info.DataSourceID == \"\" {\n\t\treturn nil, fmt.Errorf(\"database %s has no data sources\", id)\n\t}\n\treturn c.paginatePages(ctx, http.MethodPost, fmt.Sprintf(\"/v1/data_sources/%s/query\", info.DataSourceID))\n}\n\n// ResolveBlock re-fetches a single block to resolve file_upload URLs.\n// When a block contains a file_upload type, re-fetching it returns the resolved\n// download URL (temporary S3 signed URL, 1-hour expiry).\nfunc (c *notionClient) ResolveBlock(ctx context.Context, blockID string) (*notionBlock, error) {\n\trespBody, err := c.doRequest(ctx, http.MethodGet, \"/v1/blocks/\"+blockID, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar block notionBlock\n\tif err := json.Unmarshal(respBody, &block); err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshal block: %w\", err)","sourceCodeStart":336,"sourceCodeEnd":372,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/datasource/connector/notion/client.go#L336-L372","documentation":"QueryDatabaseAll first tries POST /v1/data_sources/{id}/query, and if that fails, falls back to resolving the ID as a database container via GetDatabaseInfo (GET /v1/databases/{id}). When BOTH attempts fail it wraps the data_source error and the database error together. It is thrown when the ID is neither a data_source_id nor an accessible database_id — wrong ID, missing permissions, or the object is a page/other type.","triggerScenarios":"Passing a page ID or block ID instead of a database/data_source ID; the integration lacks access to the database (401/403 on both endpoints); the database was deleted or the ID is malformed (404 on both); GetDatabaseInfo returns a network/rate-limit error during the fallback path.","commonSituations":"Copying a URL fragment that points at a page containing a database rather than the database itself; 2025 Notion API split of databases into database containers + data_sources (old database IDs no longer query directly); integration not shared with the parent page; typos when hardcoding IDs in config.","solutions":["Verify the ID is a database or data_source UUID and that the integration is connected to it (share the database in Notion's connection settings)","Use Notion search (POST /v1/search) to resolve the correct data_source_id instead of hardcoding IDs","Inspect both wrapped errors in the message: the first (%v err) is the data_source attempt, the second (%v dbErr) the database attempt — fix whichever is not object_notfound","Handle 429/rate-limit on the fallback path — the fallback GetDatabaseInfo call doubles request volume","If the database has multiple data sources, check info.DataSourceID is populated rather than empty"],"exampleFix":"// before (single hardcoded ID, fails when it's a page or unshared DB)\nrecords, err := client.QueryDatabaseAll(ctx, cfg.DatabaseID)\n// after (resolve via search, verify access up front)\ndsid, err := resolveDataSourceID(ctx, client, cfg.DatabaseOrPageURL) // uses /v1/search\nif err != nil { return fmt.Errorf(\"cannot resolve data source: %w\", err) }\ninfo, err := client.GetDataSourceInfo(ctx, dsid)\nif err != nil { return fmt.Errorf(\"no access to data source %s: %w\", dsid, err) }\nrecords, err := client.QueryDatabaseAll(ctx, dsid)","handlingStrategy":"validation","validationCode":"func ensureQueryableDatabase(ctx context.Context, client *NotionClient, id string) error {\n    info, err := client.GetDatabaseInfo(ctx, id) // resolve up front\n    if err != nil { return fmt.Errorf(\"id %s is not an accessible database: %w\", id, err) }\n    if info.DataSourceID == \"\" { return fmt.Errorf(\"database %s has no data sources\", id) }\n    return nil\n}\n// run before QueryDatabaseAll","typeGuard":"func isObjectNotFound(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"object_notfound\")\n}\n// if isObjectNotFound(dsErr) && isObjectNotFound(dbErr) -> the ID itself is wrong, not a permission issue","tryCatchPattern":"records, err := client.QueryDatabaseAll(ctx, id)\nif err != nil {\n    if strings.Contains(err.Error(), \"not a data_source\") {\n        // both paths failed: wrong ID or missing integration access\n        if err := ensureIntegrationShared(ctx, client, id); err != nil {\n            return fmt.Errorf(\"check that %s is shared with the integration: %w\", id, err)\n        }\n    }\n    return err\n}","preventionTips":["Resolve IDs via POST /v1/search at startup instead of hardcoding database IDs in config","Share every target database/page with the integration and re-verify after Notion permission changes","Remember the 2025 API split: query data_source IDs; database container IDs only resolve via GET /v1/databases/{id}","Sanity-check that the copied URL fragment points at the database itself, not its parent page"],"tags":["notion","api-request","id-resolution","permissions"],"backgroundTag":"notion-id-not-found","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}