agalwood/Motrix · critical · AggregateError
plugin locale commit and rollback both failed
Error message
plugin locale commit and rollback both failed
What it means
Thrown by PluginRegistry.commitHostLanguage: the in-memory locale switch was already applied (currentLang and per-plugin manifests/dictionaries updated, registryRevision bumped), the caller's onCommit() threw, the registry then attempted onRollback(previousLanguage), and the rollback itself also threw. Because both halves failed, the registry wraps the original error and the rollback error in an AggregateError so neither fault is hidden.
Source
Thrown at src/core/plugin/plugin-registry.ts:373
try {
onCommit()
} catch (error) {
this.currentLang = previousLanguage
for (const {
pluginId,
indexed,
manifest,
dictionaries,
} of previousEntries) {
indexed.manifest = manifest
if (dictionaries) this.localeDictionaries.set(pluginId, dictionaries)
else this.localeDictionaries.delete(pluginId)
}
this.registryRevision += 1
try {
await onRollback(previousLanguage)
} catch (rollbackError) {
throw new AggregateError(
[error, rollbackError],
'plugin locale commit and rollback both failed'
)
}
throw error
}
return true
}
/** Atomically switch registry-only consumers such as manifest list tests. */
async setHostLanguage(language: SupportedLocale): Promise<void> {
await this.setHostLanguageTransaction(language, {
commitHostLocale: () => {},
rollbackHostLocale: () => {},
})
}
/** Prepare asynchronously, then atomically commit registry + host locale. */View on GitHub (pinned to 1a708ee577)
Solutions
- Inspect AggregateError.errors[0] (original commit error) and AggregateError.errors[1] (rollback error) to identify the shared root cause.
- Fix the underlying resource both callbacks depend on (free disk, restore the IPC channel, fix permissions on userDataDir).
- Make onRollback defensive so it cannot throw for the same reason onCommit did (e.g. rollback writes to a fallback location).
- After recovery, reconcile registry state: call setHostLanguage with the desired locale again, since the registry may be left at an inconsistent revision.
Example fix
// before — onCommit and onRollback both throw on disk write
commitHostLocale: () => fs.writeFileSync(indexPath, buf),
rollbackHostLocale: () => fs.writeFileSync(indexPath, prevBuf)
// after — rollback tolerates write failure and logs instead of throwing
rollbackHostLocale: () => { try { fs.writeFileSync(indexPath, prevBuf) } catch (e) { log.error(e) } } Defensive patterns
Strategy: try-catch
Type guard
function isAggregateError(e: unknown): e is AggregateError {
return e instanceof Error && Array.isArray((e as AggregateError).errors)
} Try / catch
try {
await registry.commitHostLanguage(prepared, onCommit, onRollback)
} catch (e) {
if (e instanceof AggregateError) {
const [commitErr, rollbackErr] = e.errors
log.error('locale commit AND rollback failed', { commitErr, rollbackErr })
// registry may be inconsistent at registryRevision; reconcile by re-applying the target locale
await registry.setHostLanguage(targetLanguage).catch(() => null)
} else throw e
} Prevention
- Make onRollback resilient: never re-throw the same class of failure as onCommit; log and continue.
- Keep commit and rollback dependencies separated so one failing resource cannot break both.
- Persist locale state idempotently so a re-apply after a double fault converges.
- Back up the registry index before locale switches so you can reconcile manually.
When it happens
Trigger: commitHostLanguage() is called; onCommit() rejects/throws; the onRollback callback supplied by the caller also rejects. Reproduces whenever a host locale switch's commit hook and rollback hook both depend on the same broken resource (e.g. both write to an unwritable disk).
Common situations: Disk full or userDataDir read-only so both commit persistence and rollback persistence fail. A host-side IPC channel died, so both the commit and rollback IPC calls reject. Concurrent locale switches racing the same callbacks.
Related errors
- plugin locale prepare-commit and rollback both failed
- PLUGIN_MANIFEST_INVALID
- registry responded ${res.status}
- PLUGIN_MANIFEST_INVALID
- PLUGIN_MANIFEST_INVALID
AI-assisted analysis of agalwood/Motrix@1a708ee577 (2026-08-12).
Data as JSON: /api/errors/6a473adc34eda918.
Report an issue: GitHub.