actualbudget/actual · error
Failed to parse mapping: ${String(message)}
Error message
Failed to parse mapping: ${String(message)} What it means
mappingsFromString parses a serialized custom-sync mapping string back into nested Maps. Any exception thrown during parsing (malformed JSON, wrong shape, non-object values) is caught and rethrown wrapped in this error with the original message appended, so the root cause is preserved in the message text.
Source
Thrown at packages/loot-core/src/server/util/custom-sync-mapping.ts:27
]),
),
);
export const mappingsFromString = (str: string): Mappings => {
try {
const parsed = JSON.parse(str);
if (typeof parsed !== 'object' || parsed === null) {
throw new Error('Invalid mapping format');
}
return new Map(
Object.entries(parsed).map(([key, value]) => [
key,
new Map(Object.entries(value as object)),
]),
);
} catch (e) {
const message = e instanceof Error ? e.message : e;
throw new Error(`Failed to parse mapping: ${String(message)}`);
}
};
export const defaultMappings: Mappings = new Map([
[
'payment',
new Map([
['date', 'date'],
['payee', 'payeeName'],
['notes', 'notes'],
]),
],
[
'deposit',
new Map([
['date', 'date'],
['payee', 'payeeName'],
['notes', 'notes'],View on GitHub (pinned to d4334cb6e6)
Solutions
- Read the wrapped message — the original parse error after 'Failed to parse mapping:' pinpoints the syntax or shape problem.
- Validate the mapping string is valid JSON with shape { [category]: { [from]: to } } before calling.
- Regenerate the mapping string from the source app instead of hand-editing it.
- Fall back to defaultMappings if the custom string cannot be parsed.
Example fix
// before
const mappings = mappingsFromString(process.env.CUSTOM_SYNC_MAPPING);
// after
let mappings;
try {
mappings = mappingsFromString(process.env.CUSTOM_SYNC_MAPPING);
} catch (e) {
console.warn('Invalid mapping, using defaults:', e.message);
mappings = defaultMappings;
} Defensive patterns
Strategy: try-catch
Validate before calling
let parsed: unknown;
try { parsed = JSON.parse(mappingString); } catch { throw new Error('Mapping is not valid JSON'); }
const shapeOk = parsed && typeof parsed === 'object' &&
Object.values(parsed).every(v => v && typeof v === 'object' && !Array.isArray(v));
if (!shapeOk) throw new Error('Mapping must be { category: { from: to } }'); Type guard
function isMappingString(s: unknown): s is string {
if (typeof s !== 'string') return false;
try {
const v = JSON.parse(s);
return !!v && typeof v === 'object' &&
Object.values(v).every(x => x && typeof x === 'object' && !Array.isArray(x));
} catch { return false; }
} Try / catch
try {
mappings = mappingsFromString(raw);
} catch (e) {
if (e.message.startsWith('Failed to parse mapping:')) {
console.warn(`${e.message} — falling back to defaultMappings`);
mappings = defaultMappings;
} else { throw e; }
} Prevention
- Validate the mapping string's JSON shape before parsing
- Generate mappings programmatically instead of hand-editing them
- Keep a fallback to defaultMappings for resilience during config mistakes
When it happens
Trigger: Passing a hand-edited or corrupted mapping string (invalid JSON, top-level value that is not an object of objects), an environment variable or config file containing truncated mappings, or a mappings string produced by a different/older schema version.
Common situations: Users hand-editing custom sync mapping config; upgrades changing the mapping schema so old serialized mappings no longer match; shell escaping mangling quotes in an env-var-provided mapping string.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- No sync server configured.
- Query file must contain a JSON object
- Sync ID is required for sync ${flag}. Set --sync-id or ACTUA
- Invalid config file: expected an object with keys: ${configF
- Could not resolve on-disk budget id for syncId ${syncId} aft
AI-assisted analysis of actualbudget/actual@d4334cb6e6 (2026-08-29).
Data as JSON: /api/errors/e2ef1d0854187876.
Report an issue: GitHub.