ruvnet/RuView · error · Error

brain corpus exceeds 1 MiB

Error message

brain corpus exceeds 1 MiB

What it means

This is the firmware/privshield copy of the same daemon (byte-identical to wifi-veil/firmware/openwrt/veil_shieldd.c); the error is the event loop at lines 284-290 logging a negative return from nl_recvmsgs_default(). That libnl3 call receives and parses generic-netlink traffic on the nl80211 'mlme' group and runs the NL_CB_VALID callback veil_event_cb; any failure inside recv, parse, or callback dispatch yields a -NLE_* code. The loop whitelists only -NLE_AGAIN, so every other error (notably -NLE_INTR on signal shutdown, -NLE_SEQ_MISMATCH, -NLE_PARSE_ERR, -NLE_BAD_SOCK) aborts the daemon. Logging the raw integer and then returning exit status 0 hides both the cause and the failure from supervisors.

Source

Thrown at harness/homecore/src/brain.js:73

    if (!/^[a-f0-9]{64}$/.test(record.source?.digest || '')) {
      errors.push('source.digest must be a lowercase SHA-256 digest');
    }
  }
  if (!Array.isArray(record.tags) || record.tags.some((tag) => typeof tag !== 'string')) {
    errors.push('tags must be strings');
  }
  if ((record.content || '').length > 8192) errors.push('content exceeds 8192 characters');
  if ((record.title || '').length > 200) errors.push('title exceeds 200 characters');
  if (canonical && record.reviewed !== true) errors.push('canonical records must be reviewed');
  const combined = `${record.title || ''}\n${record.content || ''}`;
  if (SECRET.test(combined)) errors.push('record appears to contain a secret');
  if (INJECTION.test(combined)) errors.push('record contains instruction-like prompt injection');
  return errors;
}

export function loadBrain(path = CORPUS_PATH) {
  const raw = readFileSync(path, 'utf8').replace(/\r\n/g, '\n');
  if (Buffer.byteLength(raw) > 1_048_576) throw new Error('brain corpus exceeds 1 MiB');
  const records = raw.split('\n').filter(Boolean).map((line, index) => {
    if (Buffer.byteLength(line) > 16_384) {
      throw new Error(`brain line ${index + 1}: exceeds 16 KiB`);
    }
    let record;
    try {
      record = JSON.parse(line);
    } catch (error) {
      throw new Error(`brain line ${index + 1}: ${error.message}`);
    }
    const errors = validateBrainRecord(record, { canonical: true });
    if (errors.length) throw new Error(`brain line ${index + 1}: ${errors.join('; ')}`);
    return Object.freeze(record);
  });
  if (records.length > 1000) throw new Error('brain corpus exceeds 1000 records');
  const ids = new Set();
  for (const record of records) {
    if (ids.has(record.id)) throw new Error(`duplicate brain id: ${record.id}`);

View on GitHub (pinned to 4685618388)

Solutions

  1. Decode the logged integer with the libnl3 NLE_* table (1=FAILURE, 2=INTR, 3=BAD_SOCK, 5=MSGSIZE, 14=SEQ_MISMATCH, 15=DUMP_INTR, 16=PARSE_ERR, 18=NOMEM) or switch the fprintf to nl_geterror(r) so the string name appears in the log.
  2. If it fires at shutdown (-NLE_INTR after SIGINT/SIGTERM), add -NLE_INTR to the tolerated codes or test g_ctx.running before breaking - it is not a real fault.
  3. Verify veil_event_cb returns only NL_OK/NL_SKIP and never a negative errno; guard every nla_find/nla_parse result before dereferencing.
  4. Confirm nl_socket_disable_seq_check(g_ctx.sock) (line 268) is still present in this copy - it is mandatory once the mlme multicast membership at line 86-89 is active.
  5. For ENOBUFS under event storms, call nl_socket_set_buffer_size(g_ctx.sock, 1<<20, 1<<20) before entering the loop, and confirm with strace.
  6. Keep all libnl use of g_ctx.sock on one thread; no other code path may nl_socket_free or recv on it.
  7. Change the tail to return non-zero when the loop exited while still running, so init scripts detect and restart the daemon; apply any fix to BOTH veil_shieldd.c copies (wifi-veil and privshield).

Example fix

// before (firmware/privshield/openwrt/veil_shieldd.c:284-293)
while (g_ctx.running) {
    int r = nl_recvmsgs_default(g_ctx.sock);
    if (r < 0 && r != -NLE_AGAIN) {
        fprintf(stderr, "veil: nl_recvmsgs_default: %d\n", r);
        break;
    }
}
nl_socket_free(g_ctx.sock);
return 0;

// after
while (g_ctx.running) {
    int r = nl_recvmsgs_default(g_ctx.sock);
    if (r < 0 && r != -NLE_AGAIN && r != -NLE_INTR) {
        fprintf(stderr, "veil: nl_recvmsgs_default: %d (%s)\n",
                r, nl_geterror(r));
        break;
    }
}
nl_socket_free(g_ctx.sock);
return g_ctx.running ? 1 : 0; /* supervisors see the failure */
Defensive patterns

Strategy: try-catch

Validate before calling

/* Before the loop: cheap invariants that prevent the common fatal codes */
if (nl_socket_get_fd(g_ctx.sock) < 0) return 1;               /* -NLE_BAD_SOCK */
if (g_ctx.family < 0)             return 1;                   /* nl80211 unresolved */
nl_socket_set_buffer_size(g_ctx.sock, 1 << 20, 1 << 20);      /* ENOBUFS on mlme */
/* line 268 must remain: nl_socket_disable_seq_check(g_ctx.sock); */

Type guard

/* C predicate: which receive errors are safe to survive */
static int nl_recv_transient(int r) {
    return r == -NLE_AGAIN || r == -NLE_INTR || r == -NLE_DUMP_INTR;
}

Try / catch

/* Wrap each receive in an explicit error branch; classify before acting */
int r = nl_recvmsgs_default(g_ctx.sock);
if (r < 0) {
    if (nl_recv_transient(r)) continue;                /* benign: next iteration */
    fprintf(stderr, "veil: nl_recvmsgs_default: %s\n", nl_geterror(r));
    exit_code = 1;                                     /* do not mask as 0 */
    break;
}

Prevention

When it happens

Trigger: Concrete producers on this code path: (1) SIGINT/SIGTERM during the blocking receive - the handler sets g_ctx.running=0 but recvmsg returns EINTR, mapped by libnl to -NLE_INTR, which the loop treats as fatal and logs; (2) veil_event_cb returning negative after failing to parse an mlme notification - callback errors are propagated by nl_recvmsgs_default; (3) kernel ENOBUFS when mlme multicast bursts exceed the default rcvbuf, killing the loop mid-run; (4) -NLE_BAD_SOCK when the socket was closed/freed elsewhere or used from a second thread; (5) -NLE_SEQ_MISMATCH if this duplicated file drifts from the original and loses nl_socket_disable_seq_check() at line 268, which multicast events require; (6) an error ACK left on the socket by the veil_set_tx_antenna_mask() path when the driver rejects NL80211_CMD_SET_WIPHY.

Common situations: This duplicate lives in firmware/privshield/openwrt while the original sits in wifi-veil/firmware/openwrt - the classic vendored-copy failure is the two files diverging (one gets a fix, the other keeps the bug), so always patch both; running the scaffold (self-declared SYNTHETIC/L0, untested on silicon) against a real AP's mlme group; Ctrl-C or procd stop producing a scary error line on an otherwise clean shutdown; older OpenWrt libnl3 builds mapping unexpected errnos to -NLE_FAILURE, leaving the logged number ambiguous.

Related errors


AI-assisted analysis of ruvnet/RuView@4685618388 (2026-08-16). Data as JSON: /api/errors/2ae31f694eb21d63. Report an issue: GitHub.