santifer/career-ops · error · Error

--${name} must not contain tabs or newlines

Error message

--${name} must not contain tabs or newlines

What it means

Validation in the `req`/`opt` helpers of `buildRow`: no field value may contain a tab (`\t`) or newline (`\n`). Because the log is a TSV (tab-separated, one row per line), a stray tab or newline would corrupt the column structure or split a single logical row into many. Both required and optional fields are checked.

Source

Thrown at assessment-log.mjs:119

      staleFlagged,
      withoutScore: rows.filter(r => r.score === null).length,
      withoutThreshold: rows.filter(r => r.threshold === null).length,
      malformedLines: malformed,
    },
  };
}

// --- Append (`add` subcommand) ---
export function buildRow(fields, today) {
  const req = (name) => {
    const v = String(fields[name] ?? '').trim();
    if (!v) throw new Error(`--${name} is required`);
    if (v.includes('\t') || v.includes('\n')) throw new Error(`--${name} must not contain tabs or newlines`);
    return v;
  };
  const opt = (name) => {
    const v = String(fields[name] ?? '').trim();
    if (v.includes('\t') || v.includes('\n')) throw new Error(`--${name} must not contain tabs or newlines`);
    return v || '-';
  };
  const optPct = (name) => {
    const v = String(fields[name] ?? '').trim();
    if (!v) return '-';
    if (parsePct(v) === null) throw new Error(`--${name} must be a percentage (e.g. 70 or 70%), got "${v}"`);
    return v;
  };
  return [
    today, req('company'), opt('report'), req('platform'), req('subject'),
    optPct('threshold'), optPct('score'), opt('stale') === '-' ? '' : opt('stale'),
  ].join('\t');
}

function addEntry(args) {
  const fields = {};
  for (let i = 0; i < args.length; i++) {
    const m = args[i].match(/^--(company|report|platform|subject|threshold|score|stale)$/);

View on GitHub (pinned to 9b17a8ac97)

Solutions

  1. Strip/replace tabs and newlines in each value before invoking: `value.replace(/[\t\r\n]+/g, ' ')`.
  2. Keep assessment-log fields single-line by design — put long-form notes in a different artifact.
  3. Quote/escape at the shell level (`$'...'`) only if you intend spaces, but never feed real tabs.
  4. Validate in your caller: `if (/[\t\n]/.test(v)) throw ...`.
  5. Add a test asserting values containing tab/newline are rejected.

Example fix

// before
fields.company = 'Acme\tInc'; // throws
// after
fields.company = 'Acme\tInc'.replace(/[\t\n]+/g, ' '); // 'Acme Inc'
Defensive patterns

Strategy: validation

Validate before calling

function sanitizeTsv(v) {
  const s = String(v ?? '');
  if (/[	
]/.test(s)) throw new Error('Field contains tab or newline');
  return s.trim();
}
// or, to coerce rather than reject:
const clean = v.replace(/[	
]+/g, ' ');

Type guard

function isTsvSafe(v) {
  const s = String(v ?? '');
  return !/[	
]/.test(s);
}

Prevention

When it happens

Trigger: Passing `--company 'Acme\tInc'`, `--subject 'arrays\nsorting'`, or any value containing literal tab/newline characters; copying multi-line text from a clipboard; programmatically injecting user-typed free text that contains line breaks.

Common situations: Pasted values from spreadsheets that embed tabs; multiline notes accidentally routed to a single-line field; agent generating values from a model that emits formatted text with line breaks; CSV-to-TSV conversion preserving embedded newlines.

Related errors


AI-assisted analysis of santifer/career-ops@9b17a8ac97 (2026-08-13). Data as JSON: /api/errors/c7712a7842b0a76c. Report an issue: GitHub.