{"record":{"id":"29821b03df0221ec","repo":"santifer/career-ops","slug":"gmail-invalid-days-back-ctx-settings-days-ba","errorCode":null,"errorMessage":"gmail: invalid days_back \"${ctx?.settings?.days_back}\" (must be a positive integer)","messagePattern":"gmail: invalid days_back \"(.+?)\" \\(must be a positive integer\\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"plugins/gmail/index.mjs","lineNumber":83,"sourceCode":"  } catch (err) {\n    console.warn(`gmail: could not persist processed-id state — ${err.message}`);\n  }\n}\n\n/** @type {{ ingest: (ctx: any) => Promise<object[]> }} */\nexport default {\n  async ingest(ctx) {\n    const clientId = ctx?.env?.GMAIL_CLIENT_ID;\n    const clientSecret = ctx?.env?.GMAIL_CLIENT_SECRET;\n    const refreshToken = ctx?.env?.GMAIL_REFRESH_TOKEN;\n    if (!clientId || !clientSecret || !refreshToken) {\n      throw new Error('gmail: missing GMAIL_CLIENT_ID / GMAIL_CLIENT_SECRET / GMAIL_REFRESH_TOKEN in .env');\n    }\n\n    const label = ctx?.settings?.label || 'Job Leads';\n    const daysBack = Number(ctx?.settings?.days_back ?? 7);\n    if (!Number.isInteger(daysBack) || daysBack <= 0) {\n      throw new Error(`gmail: invalid days_back \"${ctx?.settings?.days_back}\" (must be a positive integer)`);\n    }\n\n    const token = await getAccessToken({ clientId, clientSecret, refreshToken }, ctx.fetch);\n    const auth = { Authorization: `Bearer ${token}` };\n    const query = `label:\"${label}\" newer_than:${daysBack}d`;\n    ctx.log(`gmail: querying ${query}`);\n\n    // List message ids (paginated). ctx.fetch throws on a non-2xx (with the body\n    // in the message), so a failed page surfaces a clear error.\n    const messages = [];\n    let pageToken = null;\n    do {\n      let url = `${GMAIL_API}/messages?q=${encodeURIComponent(query)}`;\n      if (pageToken) url += `&pageToken=${pageToken}`;\n      const data = await (await ctx.fetch(url, { headers: auth })).json();\n      if (data.messages) messages.push(...data.messages);\n      pageToken = data.nextPageToken;\n    } while (pageToken);","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/santifer/career-ops/blob/aac998c7ed7248ea853b720ceeb1fdbeb322fc5d/plugins/gmail/index.mjs#L65-L101","documentation":"The Gmail plugin validates the `days_back` setting before building its Gmail search query (`newer_than:{days}d`). It coerces the raw setting with Number() and throws if the result is not a positive integer. This catches non-numeric strings, zero, negatives, and undefined-but-present values so a malformed query is never sent to the Gmail API.","triggerScenarios":"Calling the gmail ingest with ctx.settings.days_back set to something that is not a positive integer: a string like '7d' or 'week', 0, a negative number, a float like 1.5, or an empty string. Only an outright undefined/null value is defaulted to 7 — any other bad value throws.","commonSituations":"Users put `days_back: \"last week\"` or `days_back: \"7d\"` (with unit) in their plugin settings YAML; a settings loader passes an empty string; someone copies a config where days_back was left as a placeholder or set to 0 hoping for 'today'.","solutions":["Set days_back to a plain positive integer in the plugin settings, e.g. days_back: 7 (no quotes, no units).","Remove the days_back key entirely to get the default of 7 days.","Inspect what the settings loader actually passes — log ctx.settings.days_back and its type; an empty string or 'null' string fails the check.","If loading config from YAML/JSON, ensure the value is parsed as a number, not left as a string."],"exampleFix":"// before\nsettings:\n  label: Job Leads\n  days_back: \"7d\"\n// after\nsettings:\n  label: Job Leads\n  days_back: 7","handlingStrategy":"validation","validationCode":"const raw = ctx?.settings?.days_back ?? 7;\nconst days = Number(raw);\nif (!Number.isInteger(days) || days <= 0) {\n  throw new TypeError(`days_back must be a positive integer, got: ${JSON.stringify(raw)}`);\n}","typeGuard":"const isValidDaysBack = (v) => Number.isInteger(Number(v ?? 7)) && Number(v ?? 7) > 0;","tryCatchPattern":"try {\n  await ingest(ctx);\n} catch (e) {\n  if (String(e.message).startsWith('gmail: invalid days_back')) {\n    ctx.settings.days_back = 7; // fall back to default and retry\n    await ingest(ctx);\n  } else throw e;\n}","preventionTips":["Store days_back as a bare number in YAML/JSON (no quotes, no 'd' suffix).","Omit the key entirely to use the default of 7.","Validate plugin settings at load time, before the plugin runs.","Add a schema check (e.g. zod: z.number().int().positive()) in your settings loader."],"tags":["config","validation","gmail"],"backgroundTag":"invalid-config-value","analyzedSha":"aac998c7ed7248ea853b720ceeb1fdbeb322fc5d","analyzedAt":"2026-09-16T06:35:29.214Z","contentChangedAt":"2026-09-16T06:35:29.214Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}