Skip to content

Commit b3ecf4c

Browse files
committed
feat: Enhance provider profiles loading with recovery logic for apiConfigs validation
1 parent 5662341 commit b3ecf4c

File tree

1 file changed

+93
-6
lines changed

1 file changed

+93
-6
lines changed

src/core/config/ProviderSettingsManager.ts

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -359,13 +359,100 @@ export class ProviderSettingsManager {
359359
return value
360360
})
361361

362-
// Validate the parsed content against the schema
363-
return providerProfilesSchema.parse(parsedContent)
364-
} catch (error) {
365-
if (error instanceof ZodError) {
366-
telemetryService.captureSchemaValidationError({ schemaName: "ProviderProfiles", error })
362+
// Validate the parsed content using safeParse to allow for recovery
363+
const validationResult = providerProfilesSchema.safeParse(parsedContent)
364+
365+
if (validationResult.success) {
366+
return validationResult.data
367+
} else {
368+
// Validation failed, attempt recovery if errors are only in apiConfigs
369+
const zodError = validationResult.error
370+
telemetryService.captureSchemaValidationError({ schemaName: "ProviderProfiles", error: zodError })
371+
372+
const nonApiConfigErrors = zodError.issues.filter((issue) => issue.path[0] !== "apiConfigs")
373+
374+
if (nonApiConfigErrors.length === 0 && parsedContent.apiConfigs) {
375+
// Errors are only within apiConfigs, attempt filtering
376+
try {
377+
console.warn(
378+
"ProviderSettingsManager: Invalid entries found in apiConfigs during load. Attempting to filter and recover.",
379+
JSON.stringify(
380+
zodError.issues.filter((issue) => issue.path[0] === "apiConfigs"),
381+
null,
382+
2,
383+
),
384+
)
385+
386+
const filteredApiConfigs: Record<string, ProviderSettingsWithId> = {}
387+
for (const [name, config] of Object.entries(parsedContent.apiConfigs)) {
388+
// Explicitly check if config is a valid object before parsing
389+
if (typeof config !== "object" || config === null) {
390+
console.warn(
391+
`ProviderSettingsManager: Skipping invalid profile '${name}' during load: Expected object, received ${typeof config}.`,
392+
)
393+
continue // Skip this entry entirely
394+
}
395+
// Now safeParse the object
396+
const profileValidation = providerSettingsWithIdSchema.safeParse(config)
397+
if (profileValidation.success) {
398+
filteredApiConfigs[name] = profileValidation.data
399+
} else {
400+
console.warn(
401+
`ProviderSettingsManager: Removing invalid profile '${name}' during load. Issues:`,
402+
JSON.stringify(profileValidation.error.issues, null, 2),
403+
)
404+
}
405+
}
406+
407+
// Ensure the currentApiConfigName still points to a valid config if possible
408+
let currentApiConfigName = parsedContent.currentApiConfigName
409+
// Check if the original current name exists AND is valid after filtering
410+
if (!filteredApiConfigs[currentApiConfigName]) {
411+
const originalName = parsedContent.currentApiConfigName
412+
const availableNames = Object.keys(filteredApiConfigs)
413+
// Fallback logic: try 'default', then first available, then manager's default
414+
currentApiConfigName = availableNames.includes("default")
415+
? "default"
416+
: availableNames[0] || this.defaultProviderProfiles.currentApiConfigName
417+
418+
if (originalName && originalName !== currentApiConfigName) {
419+
console.warn(
420+
`ProviderSettingsManager: Current API config '${originalName}' was invalid or removed. Switched to '${currentApiConfigName}'.`,
421+
)
422+
} else if (!originalName) {
423+
console.warn(
424+
`ProviderSettingsManager: Original currentApiConfigName was missing or invalid. Switched to '${currentApiConfigName}'.`,
425+
)
426+
}
427+
// Persisting this change immediately might be better, but requires storing here.
428+
// Let's defer persistence to the next save/store operation for simplicity.
429+
}
430+
431+
// Return a recovered object
432+
return {
433+
currentApiConfigName: currentApiConfigName,
434+
apiConfigs: filteredApiConfigs,
435+
modeApiConfigs: parsedContent.modeApiConfigs || this.defaultModeApiConfigs,
436+
migrations: parsedContent.migrations || this.defaultProviderProfiles.migrations,
437+
}
438+
} catch (recoveryError) {
439+
console.error("ProviderSettingsManager: Error occurred during recovery logic:", recoveryError)
440+
// Re-throw the recovery error to be caught by the outer catch block
441+
throw recoveryError // Ensures it's caught by the final catch
442+
}
443+
} else {
444+
// Errors exist outside apiConfigs or apiConfigs is missing, cannot recover safely
445+
console.error(
446+
"ProviderSettingsManager: Unrecoverable Zod validation failed during load. Issues:",
447+
JSON.stringify(zodError.issues, null, 2),
448+
)
449+
// Throw a specific error for unrecoverable Zod issues
450+
throw new Error(`Unrecoverable validation errors in provider profiles structure: ${zodError}`)
451+
}
367452
}
368-
453+
} catch (error) {
454+
// Catch non-Zod errors or errors during recovery logic
455+
console.error("ProviderSettingsManager: Error during load or recovery:", error)
369456
throw new Error(`Failed to read provider profiles from secrets: ${error}`)
370457
}
371458
}

0 commit comments

Comments
 (0)