santifer/career-ops · error · Error
Set NOTION_PARENT_PAGE_ID in .env (the "Career Ops" parent p
Error message
Set NOTION_PARENT_PAGE_ID in .env (the "Career Ops" parent page id).
What it means
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.
Source
Thrown at plugins/notion/_notion.mjs:90
await sleep(360); // ~3 req/s
// ctx.fetch throws on non-2xx (its message carries the body); the !r.ok
// branch below is the fallback when a plain global fetch is injected.
const r = await fetchFn(`https://api.notion.com/v1/${path}`, { method, headers: HEADERS, body: body ? JSON.stringify(body) : undefined });
const j = await r.json();
if (!r.ok) throw new Error(`Notion ${method} ${path} -> ${j.code}: ${j.message}`);
return j;
}
/** Create a page in a data source. `markdown` (optional) becomes the page body. */
async function createPage(dataSourceId, properties, markdown) {
const body = { parent: { type: 'data_source_id', data_source_id: dataSourceId }, properties };
if (markdown) body.markdown = markdown;
return api('pages', 'POST', body);
}
/** Map of DB name → primary data source id for every DB under the parent page. */
async function resolveDBs() {
if (!parent) throw new Error('Set NOTION_PARENT_PAGE_ID in .env (the "Career Ops" parent page id).');
const out = {};
let cursor;
do {
const j = await api(`blocks/${parent}/children?page_size=100${cursor ? `&start_cursor=${cursor}` : ''}`, 'GET');
for (const b of j.results) {
if (b.type !== 'child_database') continue;
const db = await api(`databases/${b.id}`, 'GET');
out[b.child_database.title] = db.data_sources?.[0]?.id;
}
cursor = j.has_more ? j.next_cursor : null;
} while (cursor);
return out;
}
async function queryDB(dataSourceId) {
let cursor, all = [];
do {
const j = await api(`data_sources/${dataSourceId}/query`, 'POST', { page_size: 100, start_cursor: cursor });View on GitHub (pinned to 9b17a8ac97)
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.
Example fix
# before (.env) NOTION_ACCESS_TOKEN=ntn_xxx # NOTION_PARENT_PAGE_ID missing # after (.env) NOTION_ACCESS_TOKEN=ntn_xxx NOTION_PARENT_PAGE_ID=4a2b1c3d4e5f60718293a4b5c6d7e8f9
Defensive patterns
Strategy: validation
Validate before calling
// Validate parent id presence and shape before constructing the client.
function resolveParent(env = process.env) {
const id = env.NOTION_PARENT_PAGE_ID;
if (!id || !/^[0-9a-f]{32}$/i.test(id.replace(/-/g, ''))) {
throw new Error('NOTION_PARENT_PAGE_ID must be a 32-char Notion page id (in .env).');
}
return id;
} Type guard
/** @param {unknown} v @returns {v is string} */
function isNotionId(v) {
return typeof v === 'string' && /^[0-9a-f-]{32,36}$/i.test(v);
} Try / catch
try {
const dbs = await client.resolveDBs();
} catch (err) {
if (err instanceof Error && err.message.includes('NOTION_PARENT_PAGE_ID')) {
console.error('Set NOTION_PARENT_PAGE_ID in .env to the Career Ops page id.');
} else throw err;
} Prevention
- 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.
When it happens
Trigger: 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}).
Common situations: 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.
Related errors
- NOTION_ACCESS_TOKEN is not set (.env) — the Notion plugin ne
- No "Applications" database found under the Career Ops page —
- gmail: missing GMAIL_CLIENT_ID / GMAIL_CLIENT_SECRET / GMAIL
- gmail: invalid days_back "${ctx?.settings?.days_back}" (must
- Notion ${method} ${path} -> ${j.code}: ${j.message}
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/0fa31d7151051a93.
Report an issue: GitHub.