santifer/career-ops · warning

apify: JD cache write failed for ${normalized.title} (${err.

Error message

apify: JD cache write failed for ${normalized.title} (${err.code || err.name}: ${err.message}); falling back to remote URL

What it means

saveJd in plugins/apify/index.mjs caches scraped job descriptions to local files using writeFileSync with flag 'wx' (fail if the file already exists). On any write error other than the handled EEXIST case, it logs 'apify: JD cache write failed ... falling back to remote URL' and returns null so the pipeline can reference the remote posting URL instead of a local capture. This is a non-fatal degradation warning, not a crash.

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 1696bec4d0)

Solutions

  1. Read the err.code in the warning (ENOENT → ensure the jds/ directory exists first, e.g. mkdirSync(dir, {recursive:true}); EACCES → fix permissions; ENOSPC → free disk space)
  2. Sanitize/shorten the job title used for the filename to avoid ENAMETOOLONG or invalid characters
  3. Ensure the script runs from the repository root (or make STATE/paths absolute) so the relative jds/ path lands in a writable location
  4. Treat the warning as informational: the pipeline continues using the remote URL; fix the environment and rerun to get a local capture

Example fix

// before
writeFileSync(filepath, content, { encoding: 'utf-8', flag: 'wx' });
// ENOENT because jds/ doesn't exist
// after: ensure the directory exists before writing
mkdirSync(path.dirname(filepath), { recursive: true });
writeFileSync(filepath, content, { encoding: 'utf-8', flag: 'wx' });
Defensive patterns

Strategy: fallback

Validate before calling

import { existsSync, accessSync, constants, statSync } from 'fs';
function cacheDirWritable(dir) {
  if (!existsSync(dir)) return false; // caller must mkdir -p
  try { accessSync(dir, constants.W_OK); return statSync(dir).isDirectory(); } catch { return false; }
}
// before saving: ensure dir exists and is writable, else expect the fallback warning

Try / catch

const relPath = saveJd(item);
if (relPath === null) {
  // cache write failed — proceed with the remote URL instead
  reference = item.url;
} else {
  reference = `local:${relPath}`;
}

Prevention

When it happens

Trigger: Calling saveJd when the target directory does not exist (ENOENT — the code creates the path from title/date but not parent dirs in all layouts), the process lacks write permission (EACCES/EPERM), the disk is full (ENOSPC), the path is invalid/too long (ENAMETOOLONG), or read-only filesystem (EROFS). EEXIST is explicitly tolerated and returns the existing relPath.

Common situations: Running the plugin in a read-only checkout or CI workspace where jds/ can't be created; disk quota exceeded after many scrapes; a job title with characters that produce an over-long or invalid filename; running from a different cwd so relative jds/ path resolves outside a writable area.

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@1696bec4d0 (2026-09-01). Data as JSON: /api/errors/a137adc9a5b2d4f7. Report an issue: GitHub.