can1357/oh-my-pi · error · TypeError
compute_fact_id: ${name} must be a str, got ${typeof value}
Error message
compute_fact_id: ${name} must be a str, got ${typeof value} What it means
computeFactId hashes (subject, predicate, object) into a deterministic fact id and requires each component to be a string. If any argument is not a string (number, null, undefined, object), it throws a TypeError naming the offending field. This guards hash determinism — a non-string would silently produce different or broken ids.
Source
Thrown at packages/mnemopi/src/core/veracity-consolidation.ts:121
}
return out;
} catch {
return [];
}
}
function nowIso(): string {
return new Date().toISOString();
}
export function computeFactId(subject: string, predicate: string, object: string): string {
for (const [name, value] of [
["subject", subject],
["predicate", predicate],
["object", object],
] as const) {
if (typeof value !== "string") {
throw new TypeError(`compute_fact_id: ${name} must be a str, got ${typeof value}`);
}
if (value === "") throw new RangeError(`compute_fact_id: ${name} must be non-empty`);
}
const chunks: Buffer[] = [];
for (const value of [subject, predicate, object]) {
const bytes = Buffer.from(value.normalize("NFC"), "utf8");
chunks.push(Buffer.from(`${bytes.length}:`, "ascii"), bytes);
}
return `cf_${createHash("sha256").update(Buffer.concat(chunks)).digest("hex").slice(0, 24)}`;
}
export function clampVeracity(raw: unknown, context = "veracity"): Veracity {
if (raw === null || raw === undefined) return "unknown";
const norm = String(raw).trim().toLowerCase();
if (norm === "") return "unknown";
if (isVeracity(norm)) return norm;
const rawString = String(raw);
const rawForLog =View on GitHub (pinned to 9690622007)
Solutions
- Coerce each component with String(value) (or fix the source data) before calling computeFactId
- Validate inputs at the boundary and skip/repair triples with non-string components
- Check the data source for null/missing columns and normalize on read
Example fix
// before const id = computeFactId(row.s, row.p, row.o); // row.o may be null // after const id = computeFactId(String(row.s), String(row.p), String(row.o ?? "")); // or skip when o == null
Defensive patterns
Strategy: type-guard
Validate before calling
function assertTripleStrings(s, p, o) {
for (const [name, v] of [["subject", s], ["predicate", p], ["object", o]]) {
if (typeof v !== "string") throw new TypeError(`compute_fact_id: ${name} must be a string`);
}
} Type guard
function isStringTriple(t) {
return typeof t.subject === "string" && typeof t.predicate === "string" && typeof t.object === "string";
} Try / catch
try {
const id = computeFactId(s, p, o);
} catch (err) {
if (err instanceof TypeError && err.message.startsWith("compute_fact_id:")) {
logger.warn("skipping triple with non-string component", { s, p, o });
return null;
}
throw err;
} Prevention
- Normalize data on read: coerce or reject non-string columns
- Type triples as {subject: string; predicate: string; object: string} end-to-end
- Handle NULL db columns explicitly instead of passing null through
- Avoid `as any` casts that hide non-string values
When it happens
Trigger: computeFactId(subject, predicate, object) called with a numeric or null component; passing database rows where a column is NULL; passing optional values without defaulting; JS callers bypassing TypeScript types.
Common situations: Loading triples from CSV/JSON where subjects were parsed as numbers; a NULL object column in SQLite mapped to null; API responses with missing fields (undefined); template-built predicates that ended up as symbols/objects.
Related errors
- Invalid pattern: {err}
- Destination option ${key} must be a string
- Destination option ${key} must be a finite number
- Destination option ${key} must be a boolean
- Destination option ${key} must be a string
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/44b0ad41f4ad6bcf.
Report an issue: GitHub.