{"record":{"id":"186303782753fcda","repo":"decolua/9router","slug":"linkup-search-requires-an-api-key","errorCode":null,"errorMessage":"Linkup Search requires an API key","messagePattern":"Linkup Search requires an API key","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"open-sse/handlers/search/callers.js","lineNumber":228,"sourceCode":"    const dateRestrictMap = { day: \"d1\", week: \"w1\", month: \"m1\", year: \"y1\" };\n    const dateRestrict = dateRestrictMap[params.timeRange];\n    if (dateRestrict) qp.set(\"dateRestrict\", dateRestrict);\n  }\n  if (typeof params.offset === \"number\" && params.offset > 0) {\n    qp.set(\"start\", String(Math.min(params.offset + 1, 91)));\n  }\n  return {\n    url: `${resolveBaseUrl(config, params)}?${qp}`,\n    init: {\n      method: \"GET\",\n      headers: { Accept: \"application/json\" },\n    },\n  };\n}\n\nfunction buildLinkupRequest(config, params) {\n  const apiKey = params.token;\n  if (!apiKey) throw new Error(\"Linkup Search requires an API key\");\n\n  const { includes, excludes } = parseDomainFilter(params.domainFilter);\n  const requestedDepth = getProviderSetting(params, \"depth\");\n  const depth =\n    requestedDepth && [\"fast\", \"standard\", \"deep\"].includes(requestedDepth)\n      ? requestedDepth\n      : \"standard\";\n\n  const body = {\n    q: params.query,\n    depth,\n    outputType: \"searchResults\",\n    maxResults: params.maxResults,\n  };\n  if (includes.length) body.includeDomains = includes;\n  if (excludes.length) body.excludeDomains = excludes;\n  if (params.timeRange && params.timeRange !== \"any\") {\n    const today = new Date();","sourceCodeStart":210,"sourceCodeEnd":246,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/handlers/search/callers.js#L210-L246","documentation":"buildLinkupRequest builds the HTTP request for the Linkup search provider. It reads the credential from params.token and refuses to proceed when it is missing, empty, or undefined, because Linkup's API authenticates every search request with a bearer API key and an unauthenticated call would just fail upstream with a 401. Throwing here gives a fast, clear failure at request-build time instead of a network round-trip.","triggerScenarios":"Calling the Linkup search route with params.token undefined, null, or an empty string — e.g. the account/credential was never configured in the dashboard, the API key field is blank, or the caller passes a params object that omitted `token`.","commonSituations":"Fresh install where the Linkup provider was enabled but no API key was pasted in; env/account store missing the LINKUP key; a code path that builds SearchRequestParams programmatically and forgets `token`; key stored under the wrong provider id so lookup returns undefined.","solutions":["Get an API key from Linkup (app.linkup.so) and configure it for the Linkup provider in the dashboard/accounts store","Verify the params object passed to the search handler actually includes token: '...' before dispatching","Check that the key is stored under the correct provider id (linkup) and is not an empty/whitespace string","If running programmatically, add a pre-check `if (!params.token) throw ...` or default the token from an env var"],"exampleFix":"// before\nconst res = await search({ provider: 'linkup', query: 'latest news' });\n// after\nconst res = await search({ provider: 'linkup', query: 'latest news', token: process.env.LINKUP_API_KEY });","handlingStrategy":"validation","validationCode":"if (!params?.token || typeof params.token !== 'string' || !params.token.trim()) {\n  throw new Error('Linkup search requires a non-empty token');\n}","typeGuard":"function hasLinkupToken(params) {\n  return typeof params?.token === 'string' && params.token.trim().length > 0;\n}","tryCatchPattern":"try {\n  const res = await search({ provider: 'linkup', query, token });\n} catch (err) {\n  if (err.message.includes('requires an API key')) {\n    // prompt for/configure the Linkup API key and retry once configured\n  } else throw err;\n}","preventionTips":["Store the Linkup API key in env/config at setup time and assert it on startup","Validate required credentials per enabled provider before dispatching any search","Keep credentials under the exact provider id the registry expects"],"tags":["api-key","search","linkup","configuration"],"backgroundTag":"missing-api-key","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}