{"record":{"id":"0fa31d7151051a93","repo":"santifer/career-ops","slug":"set-notion-parent-page-id-in-env-the-career-ops","errorCode":null,"errorMessage":"Set NOTION_PARENT_PAGE_ID in .env (the \"Career Ops\" parent page id).","messagePattern":"Set NOTION_PARENT_PAGE_ID in \\.env \\(the \"Career Ops\" parent page id\\)\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugins/notion/_notion.mjs","lineNumber":90,"sourceCode":"    await sleep(360); // ~3 req/s\n    // ctx.fetch throws on non-2xx (its message carries the body); the !r.ok\n    // branch below is the fallback when a plain global fetch is injected.\n    const r = await fetchFn(`https://api.notion.com/v1/${path}`, { method, headers: HEADERS, body: body ? JSON.stringify(body) : undefined });\n    const j = await r.json();\n    if (!r.ok) throw new Error(`Notion ${method} ${path} -> ${j.code}: ${j.message}`);\n    return j;\n  }\n\n  /** Create a page in a data source. `markdown` (optional) becomes the page body. */\n  async function createPage(dataSourceId, properties, markdown) {\n    const body = { parent: { type: 'data_source_id', data_source_id: dataSourceId }, properties };\n    if (markdown) body.markdown = markdown;\n    return api('pages', 'POST', body);\n  }\n\n  /** Map of DB name → primary data source id for every DB under the parent page. */\n  async function resolveDBs() {\n    if (!parent) throw new Error('Set NOTION_PARENT_PAGE_ID in .env (the \"Career Ops\" parent page id).');\n    const out = {};\n    let cursor;\n    do {\n      const j = await api(`blocks/${parent}/children?page_size=100${cursor ? `&start_cursor=${cursor}` : ''}`, 'GET');\n      for (const b of j.results) {\n        if (b.type !== 'child_database') continue;\n        const db = await api(`databases/${b.id}`, 'GET');\n        out[b.child_database.title] = db.data_sources?.[0]?.id;\n      }\n      cursor = j.has_more ? j.next_cursor : null;\n    } while (cursor);\n    return out;\n  }\n\n  async function queryDB(dataSourceId) {\n    let cursor, all = [];\n    do {\n      const j = await api(`data_sources/${dataSourceId}/query`, 'POST', { page_size: 100, start_cursor: cursor });","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/santifer/career-ops/blob/9b17a8ac97b398a496b38e423ae24e433b43254f/plugins/notion/_notion.mjs#L72-L108","documentation":"The Notion client needs to know which top-level page is the workspace root to enumerate its child databases (Applications, etc.). resolveDBs() paginates the children of the parent page; this guard throws if the `parent` argument is empty, before issuing the API call. The parent page id comes from ctx.env.NOTION_PARENT_PAGE_ID.","triggerScenarios":"Calling client.resolveDBs() — directly or transitively via any notion plugin operation — when NOTION_PARENT_PAGE_ID is absent from .env or passed empty to createNotionClient({parent}).","commonSituations":"User created the integration and token but never copied the parent page id (the 32-char id from the page URL); copied the page URL instead of the id (e.g. pasted the full https URL); var name typoed in .env.","solutions":["Open the \"Career Ops\" parent page in Notion and copy its id — the 32-char hex string in the URL (https://notion.so/<page-name>-<ID>, use the trailing ID segment).","Add `NOTION_PARENT_PAGE_ID=<id>` to .env.","Ensure that page is shared with the integration.","Re-run the notion plugin."],"exampleFix":"# before (.env)\nNOTION_ACCESS_TOKEN=ntn_xxx\n# NOTION_PARENT_PAGE_ID missing\n\n# after (.env)\nNOTION_ACCESS_TOKEN=ntn_xxx\nNOTION_PARENT_PAGE_ID=4a2b1c3d4e5f60718293a4b5c6d7e8f9","handlingStrategy":"validation","validationCode":"// Validate parent id presence and shape before constructing the client.\nfunction resolveParent(env = process.env) {\n  const id = env.NOTION_PARENT_PAGE_ID;\n  if (!id || !/^[0-9a-f]{32}$/i.test(id.replace(/-/g, ''))) {\n    throw new Error('NOTION_PARENT_PAGE_ID must be a 32-char Notion page id (in .env).');\n  }\n  return id;\n}","typeGuard":"/** @param {unknown} v @returns {v is string} */\nfunction isNotionId(v) {\n  return typeof v === 'string' && /^[0-9a-f-]{32,36}$/i.test(v);\n}","tryCatchPattern":"try {\n  const dbs = await client.resolveDBs();\n} catch (err) {\n  if (err instanceof Error && err.message.includes('NOTION_PARENT_PAGE_ID')) {\n    console.error('Set NOTION_PARENT_PAGE_ID in .env to the Career Ops page id.');\n  } else throw err;\n}","preventionTips":["Extract the page id from the URL's trailing segment (the 32 hex chars), not the full URL.","Validate id format at startup so a pasted URL fails fast with a clear message."],"tags":["notion","env","config","plugin"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}