JuliusBrussee/caveman · error
recordOutcome: evidence keys must be non-empty
Error message
recordOutcome: evidence keys must be non-empty
What it means
After confirming evidence is a plain object, recordOutcome iterates its entries and requires each key to be non-blank after trimming. A key of '' or ' ' is rejected because it would produce a malformed ':value' reference string on the control API. This is per-entry validation, so the first blank key aborts the whole call.
Source
Thrown at packages/mastra/src/index.ts:656
*/
export async function recordOutcome(
client: CavemanOutcomeClient,
input: RecordOutcomeInput,
): Promise<RecordOutcomeResult> {
const controlApiUrl = requireBaseUrl(client.controlApiUrl, "recordOutcome: controlApiUrl");
const token = requireNonEmpty(client.token, "recordOutcome: token");
const projectId = requireNonEmpty(client.projectId, "recordOutcome: projectId");
const taskId = requireNonEmpty(input.taskId, "recordOutcome: taskId");
const contract = requireNonEmpty(input.contract, "recordOutcome: contract");
if (!isPlainObject(input.values) || Object.keys(input.values).length === 0) {
throw new Error("recordOutcome: values must be a non-empty plain object");
}
if (!isPlainObject(input.evidence)) {
throw new Error("recordOutcome: evidence must be a plain object");
}
const evidenceRefs = Object.entries(input.evidence).map(([key, value]) => {
if (!key.trim()) throw new Error("recordOutcome: evidence keys must be non-empty");
if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
throw new Error(`recordOutcome: evidence.${key} must be a string, number, or boolean`);
}
if (typeof value === "number" && !Number.isFinite(value)) {
throw new Error(`recordOutcome: evidence.${key} must be a finite number`);
}
const ref = `${key}:${String(value)}`;
if (!ref.slice(key.length + 1)) throw new Error(`recordOutcome: evidence.${key} must be non-empty`);
return ref;
});
if (evidenceRefs.length === 0) {
throw new Error("recordOutcome: evidence must contain at least one reference");
}
if (evidenceRefs.length > MAX_EVIDENCE_REFS) {
throw new Error(`recordOutcome: evidence must contain at most ${MAX_EVIDENCE_REFS} references`);
}
const confidence = input.confidence;View on GitHub (pinned to 27d5a3981a)
Solutions
- Give every evidence entry a real label: { commit: sha, tests: 12 }.
- Filter/validate computed keys before the call: skip entries whose trimmed key is empty.
- If the key source may be blank, default it (e.g. key.trim() || 'unnamed').
Example fix
// before
await recordOutcome(client, { taskId, contract, values, evidence: { [label]: sha } }); // label === ""
// after
const key = label.trim() || "ref";
await recordOutcome(client, { taskId, contract, values, evidence: { [key]: sha } }); Defensive patterns
Strategy: validation
Validate before calling
function evidenceKeysValid(evidence: Record<string, unknown>): boolean {
return Object.keys(evidence).every(k => k.trim().length > 0);
} Try / catch
try {
await recordOutcome(client, input);
} catch (e) {
if (e instanceof Error && e.message.includes("evidence keys must be non-empty")) {
const cleaned = Object.fromEntries(Object.entries(input.evidence).filter(([k]) => k.trim()));
return recordOutcome(client, { ...input, evidence: cleaned });
}
throw e;
} Prevention
- Sanitize computed keys: default blank labels to a fallback name before use.
- Never build evidence keys from unvalidated parsed text (split lines, filenames).
- Filter empty-key entries out of dynamic payloads before calling recordOutcome.
When it happens
Trigger: evidence: { '': 'x' } or evidence: { ' ': 1 }, commonly produced by computed keys like { [someVar]: val } where someVar is an empty/whitespace string.
Common situations: Building keys from variables (file names, step labels) that can be empty, parsing key/value pairs out of text where a separator produces an empty left side, or JSON payloads with "" keys round-tripped into evidence.
Related errors
- recordOutcome: values must be a non-empty plain object
- recordOutcome: evidence must be a plain object
- option not found
- cave_harness_adapter_version_invalid
- cavemanExporterConfig: protocol ${protocol} is not accepted
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/3a312f4dceaf0163.
Report an issue: GitHub.