santifer/career-ops · warning
apify: JD cache write failed for
Error message
apify: JD cache write failed for ${normalized.title} (${err.code || err.name}: ${err.message}); falling back to remote URL What it means
Warning in plugins/apify/index.mjs's saveJd: writing the normalized JD markdown capture into the jds/ cache with flag 'wx' failed for a reason other than EEXIST. The plugin logs the error code/name plus message and returns null so the pipeline keeps the remote URL instead of the local capture.
Solutions
- Create the cache directory: mkdir -p jds
- Sanitize normalized.title before building filepath (strip '/', ':', control chars)
- Check err.code in the warning: EACCES → fix permissions, ENOSPC → free disk space
- Re-run the plugin; failure is non-fatal (remote URL fallback keeps the pipeline moving)
Example fix
// before
writeFileSync(filepath, content, { encoding: 'utf-8', flag: 'wx' });
// after
mkdirSync(dirname(filepath), { recursive: true });
const safeName = normalized.title.replace(/[^a-z0-9-_ ]/gi, '-').slice(0, 80);
writeFileSync(filepath, content, { encoding: 'utf-8', flag: 'wx' }); Defensive patterns
Strategy: validation
Validate before calling
import { mkdirSync } from 'fs';
mkdirSync('jds', { recursive: true }); // before any saveJd call
const safe = title.replace(/[^\w\-. ]+/g, '-').slice(0, 100); Type guard
function safeFilename(name) { return typeof name === 'string' && name.replace(/[\\/:*?"<>|]/g, '').trim().length > 0 ? name : 'untitled'; } Try / catch
try { writeFileSync(filepath, content, { flag: 'wx' }); } catch (err) { if (err.code === 'EEXIST') return relPath; console.warn(`JD cache write failed: ${err.message}`); return null; } Prevention
- Create jds/ at plugin startup, not per write
- Sanitize job titles (strip slashes, control chars) before building filenames
- Use 'wx' intentionally: treat EEXIST as a cache hit, not an error
- Watch for ENOSPC in long Apify batch runs
When it happens
Trigger: writeFileSync(flag:'wx') throws ENOENT (jds/ directory doesn't exist yet), EACCES (no write permission), or ENOSPC; relPath was not set so even EEXIST can't be short-circuited.
Common situations: Fresh clone without a jds/ directory; read-only volume; job titles with characters that break path construction (slashes) producing an invalid filepath; disk quota exceeded during a large Apify run.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- ⚠️ Could not release report reservation
- ⚠️ Could not release report reservation
- ⚠️ Could not save HTML
- ⚠️ Could not save report
- ⚠️ Could not save report
AI-assisted analysis of santifer/career-ops@aac998c7ed (2026-09-16).
Data as JSON: /api/errors/a137adc9a5b2d4f7.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/apify/index.mjs:153
const today = new Date().toISOString().slice(0, 10);
const content = `---
title: ${yamlEscape(normalized.title)}
company: ${yamlEscape(normalized.company)}
url: ${yamlEscape(normalized.url)}
location: ${yamlEscape(normalized.location)}
scraped: "${today}"
source: ${sourceLabel}
---
# ${normalized.title} — ${normalized.company}
${descriptionBody}
`;
writeFileSync(filepath, content, { encoding: 'utf-8', flag: 'wx' });
return relPath;
} catch (err) {
if (err?.code === 'EEXIST' && relPath) return relPath;
console.warn(`apify: JD cache write failed for ${normalized.title} (${err.code || err.name}: ${err.message}); falling back to remote URL`);
return null;
}
}
export function normalizeItem(item, fieldMap, defaults) {
const out = {
title: String(pickField(item, fieldMap.title) || ''),
url: String(pickField(item, fieldMap.url) || ''),
company: fieldMap.company ? String(pickField(item, fieldMap.company) || '') : '',
location: fieldMap.location ? String(pickField(item, fieldMap.location) || '') : '',
};
for (const [k, v] of Object.entries(defaults || {})) {
if (!ALLOWED_DEFAULT_KEYS.has(k)) continue;
if (!out[k]) out[k] = String(v);
}
return out;
}
View on GitHub (pinned to aac998c7ed)