santifer/career-ops · error · Error
gmail: invalid days_back "${ctx?.settings?.days_back}" (must
Error message
gmail: invalid days_back "${ctx?.settings?.days_back}" (must be a positive integer) What it means
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.
Source
Thrown at plugins/gmail/index.mjs:83
} catch (err) {
console.warn(`gmail: could not persist processed-id state — ${err.message}`);
}
}
/** @type {{ ingest: (ctx: any) => Promise<object[]> }} */
export default {
async ingest(ctx) {
const clientId = ctx?.env?.GMAIL_CLIENT_ID;
const clientSecret = ctx?.env?.GMAIL_CLIENT_SECRET;
const refreshToken = ctx?.env?.GMAIL_REFRESH_TOKEN;
if (!clientId || !clientSecret || !refreshToken) {
throw new Error('gmail: missing GMAIL_CLIENT_ID / GMAIL_CLIENT_SECRET / GMAIL_REFRESH_TOKEN in .env');
}
const label = ctx?.settings?.label || 'Job Leads';
const daysBack = Number(ctx?.settings?.days_back ?? 7);
if (!Number.isInteger(daysBack) || daysBack <= 0) {
throw new Error(`gmail: invalid days_back "${ctx?.settings?.days_back}" (must be a positive integer)`);
}
const token = await getAccessToken({ clientId, clientSecret, refreshToken }, ctx.fetch);
const auth = { Authorization: `Bearer ${token}` };
const query = `label:"${label}" newer_than:${daysBack}d`;
ctx.log(`gmail: querying ${query}`);
// List message ids (paginated). ctx.fetch throws on a non-2xx (with the body
// in the message), so a failed page surfaces a clear error.
const messages = [];
let pageToken = null;
do {
let url = `${GMAIL_API}/messages?q=${encodeURIComponent(query)}`;
if (pageToken) url += `&pageToken=${pageToken}`;
const data = await (await ctx.fetch(url, { headers: auth })).json();
if (data.messages) messages.push(...data.messages);
pageToken = data.nextPageToken;
} while (pageToken);View on GitHub (pinned to 9b17a8ac97)
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.
Example fix
# before (config/plugins.yml) gmail: enabled: true label: "Job Leads" days_back: "7d" # after gmail: enabled: true label: "Job Leads" days_back: 7
Defensive patterns
Strategy: validation
Validate before calling
// Validate settings.days_back before letting the plugin read it.
function validDaysBack(settings) {
const raw = settings?.days_back ?? 7;
const n = Number(raw);
return Number.isInteger(n) && n > 0 ? n : null;
}
const days = validDaysBack(ctx.settings);
if (days === null) throw new Error(`config error: gmail.days_back must be a positive integer, got ${JSON.stringify(ctx.settings?.days_back)}`); Type guard
/** @param {unknown} v @returns {v is number} */
function isPositiveInt(v) {
return typeof v === 'number' && Number.isInteger(v) && v > 0;
} Try / catch
try {
await gmailPlugin.ingest(ctx);
} catch (err) {
if (err instanceof Error && err.message.startsWith('gmail: invalid days_back')) {
ctx.log('Fix config/plugins.yml: gmail.days_back must be a positive integer (days), e.g. 7.');
} else throw err;
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- Unsupported profile photo style: ${style} (expected rounded,
- Malformed benchmarks file at ${path}: expected a top-level "
- apify: entry ${entry.name} missing 'actor' (e.g. misceres/in
- apify: entry ${entry.name} has invalid field_map. Each of ti
- gmail: missing GMAIL_CLIENT_ID / GMAIL_CLIENT_SECRET / GMAIL
AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13).
Data as JSON: /api/errors/29821b03df0221ec.
Report an issue: GitHub.