{"record":{"id":"61e5997ee42dd1c5","repo":"Tencent/WeKnora","slug":"database-s-has-no-data-sources","errorCode":null,"errorMessage":"database %s has no data sources","messagePattern":"database (.+?) has no data sources","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/datasource/connector/notion/client.go","lineNumber":357,"sourceCode":"}\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)\n\t}\n\treturn &block, nil\n}","sourceCodeStart":339,"sourceCodeEnd":375,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/datasource/connector/notion/client.go#L339-L375","documentation":"QueryDatabaseAll queries a Notion database by trying the id as a data_source_id first; on 404 it falls back to treating the id as a database container ID and resolves it via GET /v1/databases/{id}. If that database object exists but its data_sources list is empty (info.DataSourceID == \"\"), the client cannot identify any data source to POST /v1/data_sources/{id}/query against, so it throws this error. In practice this means the id is a valid database container but Notion's API model exposes no queryable data source for it (API-version drift or an empty/deleted database).","triggerScenarios":"Calling QueryDatabaseAll(ctx, id) where id is a database container ID (e.g. from a child_database block) whose GET /v1/databases/{id} response has an empty data_sources array — i.e. info.DataSourceID is empty. Requires the initial data_source query attempt to have failed with 404 first.","commonSituations":"Using an old database_id cached before Notion moved querying to the data_sources endpoint (API version 2022-06-28 vs newer 2025-09-03-style layouts); the database was emptied/deleted so its only data source was removed; copying a database container URL id instead of the data_source id from search results.","solutions":["Re-fetch the database id from the Notion search endpoint or a fresh child_database block to get a current data_source_id and pass that to QueryDatabaseAll.","Open the database in Notion and confirm it still contains at least one data source (not deleted/emptied); restore or recreate it if so.","If you control the integration, pin a Notion API version where databases still expose data_sources, or update notionBlock/notionDatabase parsing so DataSourceID is extracted from the new response shape.","Check that GetDatabaseInfo unmarshals the data_sources array correctly — an empty DataSourceID may indicate a struct-field mismatch (e.g. wrong JSON tag) rather than a truly empty database."],"exampleFix":"// before\nrecords, err := client.QueryDatabaseAll(ctx, \"a1b2c3d4e5f6...\" ) // old cached database container id\n// after\ndataSourceID := \"f6e5d4c3b2a1...\" // from search result or block data_source.id\nrecords, err := client.QueryDatabaseAll(ctx, dataSourceID)","handlingStrategy":"validation","validationCode":"if id == \"\" {\n    return fmt.Errorf(\"database id is required\")\n}\n// prefer data_source ids obtained from search / child_database blocks:\nif !strings.Contains(id, \"data_source\") && dataSourceID != \"\" {\n    id = dataSourceID\n}","typeGuard":"func hasDataSource(info *notionDatabaseInfo) bool {\n    return info != nil && info.DataSourceID != \"\"\n}","tryCatchPattern":"records, err := client.QueryDatabaseAll(ctx, id)\nif err != nil {\n    if strings.Contains(err.Error(), \"has no data sources\") {\n        // fall back to re-resolving the id via search or skip the database\n        logger.Warnf(ctx, \"skipping database %s: no data sources\", id)\n        return nil\n    }\n    return err\n}","preventionTips":["Store and pass data_source_id (not the database container id) whenever the API surface provides one.","Re-resolve ids from search/blocks instead of caching database ids across long-lived jobs.","Before querying, call GetDatabaseInfo and check DataSourceID != \"\" to fail fast with a clearer message."],"tags":["notion","database","api-versioning","data-source"],"backgroundTag":"missing-data-source-id","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}