
























@@ -2084,6 +2084,7 @@ export async function detectLegacyStateMigrations(params: {
20842084async function migrateLegacySessions(
20852085detected: LegacyStateDetection,
20862086now: () => number,
2087+options: { recoverCorruptTargetStore?: boolean } = {},
20872088): Promise<{ changes: string[]; warnings: string[] }> {
20882089const changes: string[] = [];
20892090const warnings: string[] = [];
@@ -2128,11 +2129,12 @@ async function migrateLegacySessions(
21282129agentId: detected.targetAgentId,
21292130mainKey: detected.targetMainKey,
21302131});
2132+let migratedDirectChatKey: string | undefined;
21312133if (!merged[mainKey]) {
21322134const latest = pickLatestLegacyDirectEntry(legacyStore);
21332135if (latest?.sessionId) {
21342136merged[mainKey] = latest;
2135-changes.push(`Migrated latest direct-chat session → ${mainKey}`);
2137+migratedDirectChatKey = mainKey;
21362138}
21372139}
21382140@@ -2142,7 +2144,29 @@ async function migrateLegacySessions(
21422144);
21432145}
214421462147+const targetExists = fileExists(detected.sessions.targetStorePath);
2148+let targetReadable = !targetExists || targetParsed.ok;
2149+if (!targetReadable) {
2150+if (options.recoverCorruptTargetStore) {
2151+const archivedTargetPath = `${detected.sessions.targetStorePath}.corrupt-${now()}`;
2152+try {
2153+fs.renameSync(detected.sessions.targetStorePath, archivedTargetPath);
2154+changes.push(`Archived corrupt target sessions store → ${archivedTargetPath}`);
2155+targetReadable = true;
2156+} catch (err) {
2157+warnings.push(
2158+`Target sessions store unreadable; failed to archive ${detected.sessions.targetStorePath}: ${String(err)}`,
2159+);
2160+}
2161+} else {
2162+warnings.push(
2163+`Target sessions store unreadable; left untouched to avoid overwriting at ${detected.sessions.targetStorePath}. Run openclaw doctor --fix to archive it and retry the legacy merge.`,
2164+);
2165+}
2166+}
2167+21452168if (
2169+targetReadable &&
21462170(legacyParsed.ok || targetParsed.ok) &&
21472171(Object.keys(legacyStore).length > 0 || Object.keys(targetStore).length > 0)
21482172) {
@@ -2157,12 +2181,19 @@ async function migrateLegacySessions(
21572181await saveSessionStore(detected.sessions.targetStorePath, normalized, {
21582182skipMaintenance: true,
21592183});
2184+if (migratedDirectChatKey) {
2185+changes.push(`Migrated latest direct-chat session → ${migratedDirectChatKey}`);
2186+}
21602187changes.push(`Merged sessions store → ${detected.sessions.targetStorePath}`);
21612188if (canonicalizedTarget.legacyKeys.length > 0) {
21622189changes.push(`Canonicalized ${canonicalizedTarget.legacyKeys.length} legacy session key(s)`);
21632190}
21642191}
216521922193+if (!targetReadable) {
2194+return { changes, warnings };
2195+}
2196+21662197const entries = safeReadDir(detected.sessions.legacyDir);
21672198for (const entry of entries) {
21682199if (!entry.isFile()) {
@@ -2184,7 +2215,7 @@ async function migrateLegacySessions(
21842215}
21852216}
218622172187-if (legacyParsed.ok) {
2218+if (legacyParsed.ok && targetReadable) {
21882219try {
21892220if (fileExists(detected.sessions.legacyStorePath)) {
21902221fs.rmSync(detected.sessions.legacyStorePath, { force: true });
@@ -2291,6 +2322,7 @@ export async function runLegacyStateMigrations(params: {
22912322detected: LegacyStateDetection;
22922323config?: OpenClawConfig;
22932324now?: () => number;
2325+recoverCorruptTargetStore?: boolean;
22942326}): Promise<{ changes: string[]; warnings: string[] }> {
22952327const now = params.now ?? (() => Date.now());
22962328const detected = params.detected;
@@ -2307,7 +2339,9 @@ export async function runLegacyStateMigrations(params: {
23072339 detected,
23082340config: params.config ?? ({} as OpenClawConfig),
23092341});
2310-const sessions = await migrateLegacySessions(detected, now);
2342+const sessions = await migrateLegacySessions(detected, now, {
2343+recoverCorruptTargetStore: params.recoverCorruptTargetStore,
2344+});
23112345const agentDir = await migrateLegacyAgentDir(detected, now);
23122346const channelPlans = await runLegacyMigrationPlans(
23132347detected.channelPlans.plans.filter((plan) => plan.kind !== "plugin-state-import"),
@@ -2512,6 +2546,7 @@ export async function autoMigrateLegacyState(params: {
25122546homedir?: () => string;
25132547log?: MigrationLogger;
25142548now?: () => number;
2549+recoverCorruptTargetStore?: boolean;
25152550}): Promise<{
25162551migrated: boolean;
25172552skipped: boolean;
@@ -2635,7 +2670,9 @@ export async function autoMigrateLegacyState(params: {
26352670 detected,
26362671config: params.cfg,
26372672});
2638-const sessions = await migrateLegacySessions(detected, now);
2673+const sessions = await migrateLegacySessions(detected, now, {
2674+recoverCorruptTargetStore: params.recoverCorruptTargetStore,
2675+});
26392676const agentDir = await migrateLegacyAgentDir(detected, now);
26402677const channelPlans = await runLegacyMigrationPlans(
26412678detected.channelPlans.plans.filter((plan) => plan.kind !== "plugin-state-import"),
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。