santifer/career-ops · error · Error

No "Applications" database found under the Career Ops page…

Error message

No "Applications" database found under the Career Ops page — create it and share the integration with it.

What it means

The Notion plugin for career-ops looks up a database literally named 'Applications' among the databases shared with the integration and nested under the Career Ops page (via client.resolveDBs()). This error is thrown when the integration can authenticate and list databases but none matches that name. It means the Notion workspace setup is incomplete, not a code or auth failure.

Solutions

  1. Create a database titled exactly 'Applications' (case-sensitive) as a child of the Career Ops page in Notion
  2. Share the database (and its parent page) with the integration: in Notion open the page -> ... menu -> Connections -> add your integration
  3. Verify the database title has no leading/trailing spaces or alternate naming, then re-run the sync
  4. If the database lives elsewhere, move it under the Career Ops page or share it directly with the integration so resolveDBs() can enumerate it
Defensive patterns

Strategy: validation

Validate before calling

// Before any Notion sync, confirm the DB is reachable
const dbs = await client.resolveDBs();
if (!dbs['Applications']) {
  throw new Error('Notion: create an "Applications" database under the Career Ops page and share the integration with it.');
}

Type guard

function hasApplicationsDb(dbs) {
  return dbs != null && typeof dbs === 'object' && 'Applications' in dbs;
}

Try / catch

try {
  await notionSyncTracker();
} catch (e) {
  if (e.message.includes('No "Applications" database found')) {
    console.error('Setup incomplete: create the "Applications" DB under the Career Ops page and connect the integration (Notion -> page -> Connections).');
  } else throw e;
}

Prevention

When it happens

Trigger: Calling applicationsDb(client) (used by every Notion sync operation) when the 'Applications' database does not exist under the Career Ops page, or exists but was not shared with the integration, or is named differently (e.g. 'Applications tracker', extra whitespace, different capitalization).

Common situations: Fresh install where the user created the Career Ops page but not the database; the database lives on another page or in another team space not shared with the integration; the database was renamed or recreated after initial setup; the integration received access to the page but the database was moved out from under it; a typo in the database title when creating it manually.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16). Data as JSON: /api/errors/b0ba2aa1a7fd8665. Report an issue: GitHub.

Appendix: source

Thrown at plugins/notion/index.mjs:37

// internal integration. Enable in config/plugins.yml; keys in .env.
//
//   node plugins.mjs run notion export            # mirror tracker → Notion
//   node plugins.mjs run notion search "platform" # read matching records → pipeline

import { createNotionClient, rich, canonicalStatus } from './_notion.mjs';

function clientFromCtx(ctx) {
  return createNotionClient({
    token: ctx?.env?.NOTION_ACCESS_TOKEN,
    parent: ctx?.env?.NOTION_PARENT_PAGE_ID,
    fetch: ctx?.fetch, // route through the engine's allowedHosts/redirect guard
  });
}

async function applicationsDb(client) {
  const dbs = await client.resolveDBs();
  const apps = dbs['Applications'];
  if (!apps) throw new Error('No "Applications" database found under the Career Ops page — create it and share the integration with it.');
  return apps;
}

/**
 * Parse a tracker score cell into a numeric value for the Notion DB Score property.
 *
 * Scores in applications.md may be formatted like `4.2/5`, `**4.2/5**`, `4.25`, etc.
 * Strips formatting and extracts the first numeric value so slash-formatted
 * scores (e.g. 4.2/5) are not mangled into 4.25 (#1414).
 *
 * @param {unknown} s - Raw score value from tracker row.
 * @returns {number} Parsed score, or NaN if no valid number is present.
 */
export function parseScore(s) {
  const m = String(s ?? '').replace(/\*\*/g, '').match(/([\d.]+)/);
  return m ? parseFloat(m[1]) : NaN;
}

View on GitHub (pinned to aac998c7ed)