decolua/9router · warning

[DATA_DIR] '${configured}' not writable → fallback ~/.${APP_

Error message

[DATA_DIR] '${configured}' not writable → fallback ~/.${APP_NAME}

What it means

getDataDir() honors a user-configured DATA_DIR env var; if creating that directory fails with EACCES or EPERM, it warns and falls back to the default per-user directory (~/.9router, or %APPDATA%\9router on Windows). All MITM/state files then live in the fallback location instead of the configured one. Other mkdir errors (e.g. ENOTDIR, invalid path) are re-thrown.

Source

Thrown at src/mitm/paths.js:22

const APP_NAME = "9router";

function defaultDir() {
  if (process.platform === "win32") {
    return path.join(process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming"), APP_NAME);
  }
  return path.join(os.homedir(), `.${APP_NAME}`);
}

function getDataDir() {
  const configured = process.env.DATA_DIR;
  if (!configured) return defaultDir();
  try {
    fs.mkdirSync(configured, { recursive: true });
    return configured;
  } catch (e) {
    if (e?.code === "EACCES" || e?.code === "EPERM") {
      console.warn(`[DATA_DIR] '${configured}' not writable → fallback ~/.${APP_NAME}`);
      return defaultDir();
    }
    throw e;
  }
}

const DATA_DIR = getDataDir();
const MITM_DIR = path.join(DATA_DIR, "mitm");

module.exports = { DATA_DIR, MITM_DIR };

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Fix permissions on the configured DATA_DIR: chown/chmod it so the running user can write (sudo chown -R $USER <dir>).
  2. Pick a writable DATA_DIR (e.g. /var/lib/9router with proper ownership, or a user-owned path) and restart.
  3. If the fallback is acceptable, unset DATA_DIR or copy old data from the configured dir into ~/.9router to keep history.
  4. If the error persists as a non-EACCES crash, fix the invalid path value in DATA_DIR (trailing garbage, wrong drive).

Example fix

// before
DATA_DIR=/var/lib/9router   # owned by root → EACCES fallback
// after
sudo mkdir -p /var/lib/9router && sudo chown $USER /var/lib/9router
export DATA_DIR=/var/lib/9router
Defensive patterns

Strategy: validation

Validate before calling

// validate DATA_DIR before starting the app
import fs from "fs";
const dir = process.env.DATA_DIR;
if (dir) {
  try {
    fs.mkdirSync(dir, { recursive: true });
    fs.accessSync(dir, fs.constants.W_OK);
    console.log("DATA_DIR OK:", dir);
  } catch (e) {
    console.error("DATA_DIR not writable:", e.code); // expect fallback to ~/.9router
  }
}

Prevention

When it happens

Trigger: DATA_DIR env is set but mkdirSync(configured, {recursive:true}) fails with EACCES/EPERM: path owned by another user, read-only mount, sandbox/container restrictions, or a protected system path.

Common situations: Docker volume mounted read-only; running the app as non-root with DATA_DIR=/etc or /var/9router owned by root; Windows Program Files as target; systemd service with a hardened ProtectHome/ReadOnlyPaths; macOS sandbox denying a custom path.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/c49a6705fec1ae41. Report an issue: GitHub.