{"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/9b17a8ac97b398a496b38e423ae24e433b43254f/plugins/gmail/index.mjs#L65-L101","documentation":"The Gmail plugin reads settings.days_back from config/plugins.yml (ctx.settings) to scope the Gmail query to `newer_than:Nd`. It must coerce to a positive integer; the default is 7. This error fires when Number(settings.days_back) is non-integer or <= 0. Note the error string interpolates the RAW settings value (ctx?.settings?.days_back), not the coerced Number, so the message shows exactly what was configured.","triggerScenarios":"config/plugins.yml sets `gmail.days_back` to a non-numeric string (e.g. \"one\"), a float (e.g. 2.5), zero, a negative number, or null/non-string that Number() rejects (→ NaN, which fails Number.isInteger). Also when days_back is unset AND the `?? 7` default is somehow bypassed (it isn't, but a malformed override can shadow it).","commonSituations":"Typo in plugins.yml (days_back: \"7d\" — the unit is implicit, not appended); copy-pasting a duration string from another tool; setting days_back: 0 thinking it disables the filter.","solutions":["Edit config/plugins.yml and set `days_back` to a positive integer (e.g. 7, 14, 30). The unit is days and is applied automatically — do NOT append 'd'.","If you want the default, simply remove the days_back key (defaults to 7).","Re-run the gmail plugin."],"exampleFix":"# before (config/plugins.yml)\ngmail:\n  enabled: true\n  label: \"Job Leads\"\n  days_back: \"7d\"\n\n# after\ngmail:\n  enabled: true\n  label: \"Job Leads\"\n  days_back: 7","handlingStrategy":"validation","validationCode":"// Validate settings.days_back before letting the plugin read it.\nfunction validDaysBack(settings) {\n  const raw = settings?.days_back ?? 7;\n  const n = Number(raw);\n  return Number.isInteger(n) && n > 0 ? n : null;\n}\nconst days = validDaysBack(ctx.settings);\nif (days === null) throw new Error(`config error: gmail.days_back must be a positive integer, got ${JSON.stringify(ctx.settings?.days_back)}`);","typeGuard":"/** @param {unknown} v @returns {v is number} */\nfunction isPositiveInt(v) {\n  return typeof v === 'number' && Number.isInteger(v) && v > 0;\n}","tryCatchPattern":"try {\n  await gmailPlugin.ingest(ctx);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('gmail: invalid days_back')) {\n    ctx.log('Fix config/plugins.yml: gmail.days_back must be a positive integer (days), e.g. 7.');\n  } else throw err;\n}","preventionTips":["Treat config files as untrusted input — schema-validate plugins.yml at load time (ajv/zod).","Use a JSON schema that rejects strings/floats/non-positives for days_back.","Add a unit test for the days_back coercion covering '7d', 0, 2.5, undefined."],"tags":["gmail","config","validation","plugin"],"backgroundTag":null,"analyzedSha":"9b17a8ac97b398a496b38e423ae24e433b43254f","analyzedAt":"2026-08-13T00:48:39.135Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}