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

  1. Create the cache directory: mkdir -p jds
  2. Sanitize normalized.title before building filepath (strip '/', ':', control chars)
  3. Check err.code in the warning: EACCES → fix permissions, ENOSPC → free disk space
  4. 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

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


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)