siyuan-note/siyuan · error
invalid provider config
Error message
invalid provider config
What it means
After unmarshalling and ai.Normalize(), resolveAIProvider expects exactly one provider to remain in the constructed conf.AI; if normalization dropped or duplicated the provider (len != 1), the inline config is considered structurally invalid and rejected.
Solutions
- Compare your providerConfig against a known-good saved provider (fetch one from settings/config) and match all required fields
- Log the provider struct before ai.Normalize() to see what Normalize drops or transforms
- Upgrade/downgrade concerns: check whether conf.Provider/Normalize semantics changed between versions and update the payload
- Send the provider ID via request.Provider instead of inline config if you mean to reference an already-saved provider
Example fix
// before: wrapper object unmarshals to zero providers
{"providerConfig": {"provider": {"baseURL": "https://api.openai.com"}}}
// after: flat provider object
{"providerConfig": {"baseURL": "https://api.openai.com", "key": "sk-..."}} Defensive patterns
Strategy: validation
Validate before calling
ai := &conf.AI{Providers: []*conf.Provider{provider}}
ai.Normalize()
if len(ai.Providers) != 1 {
return errors.New("invalid provider config")
} Try / catch
provider, err := resolveAIProvider(req)
if err != nil {
return contractFailure[string](gulu.Ret.NewResultWithMsg(err.Error()))
} Prevention
- Mirror every field a known-good saved provider carries in inline configs
- Test providerConfig against conf.AI.Normalize before deploying changes
- Prefer provider-ID references over inline configs when the provider is already saved
When it happens
Trigger: An inline providerConfig that conf.AI.Normalize() discards — e.g. a provider failing normalization's validity checks (bad/missing fields beyond BaseURL), or a payload shape that unmarshals into zero or multiple providers.
Common situations: Partially filled provider configs from older versions whose fields no longer survive Normalize, API clients sending an unexpected providerConfig shape (array or wrapper object), config migrations leaving the provider in a state Normalize rejects.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- provider base URL is required
- AI editor action must not be empty
- AI editor action not found
- generate image failed
- provider not found
AI-assisted analysis of siyuan-note/siyuan@9f775e8a12 (2026-09-19).
Data as JSON: /api/errors/bb9cba44f304cdb9.
Report an issue: GitHub.
Appendix: source
Thrown at kernel/api/ai.go:77
if providerConfig := request.ProviderConfig; providerConfig != nil {
data, err := gulu.JSON.MarshalJSON(providerConfig)
if err != nil {
return nil, err
}
provider := &conf.Provider{}
if err = gulu.JSON.UnmarshalJSON(data, provider); err != nil {
return nil, err
}
if strings.TrimSpace(provider.BaseURL) == "" {
return nil, errors.New("provider base URL is required")
}
if err = util.ValidateAIProviderHeaders(provider.Headers); err != nil {
return nil, err
}
ai := &conf.AI{Providers: []*conf.Provider{provider}}
ai.Normalize()
if len(ai.Providers) != 1 {
return nil, errors.New("invalid provider config")
}
return ai.Providers[0], nil
}
providerID := request.Provider
for _, provider := range model.Conf.AI.Providers {
if provider != nil && provider.ID == providerID {
return provider, nil
}
}
return nil, errors.New("provider not found")
}
var chatGPT = contractHandler(apicontract.AIChatGPT, chatGPTContract)
func chatGPTContract(c *gin.Context, req apicontract.AIMessageRequest) apicontract.Response[string] {
return apicontract.Success(model.ChatGPT(req.Msg))View on GitHub (pinned to 9f775e8a12)