{"record":{"id":"bb9cba44f304cdb9","repo":"siyuan-note/siyuan","slug":"invalid-provider-config","errorCode":null,"errorMessage":"invalid provider config","messagePattern":"invalid provider config","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/api/ai.go","lineNumber":77,"sourceCode":"\tif providerConfig := request.ProviderConfig; providerConfig != nil {\n\t\tdata, err := gulu.JSON.MarshalJSON(providerConfig)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tprovider := &conf.Provider{}\n\t\tif err = gulu.JSON.UnmarshalJSON(data, provider); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif strings.TrimSpace(provider.BaseURL) == \"\" {\n\t\t\treturn nil, errors.New(\"provider base URL is required\")\n\t\t}\n\t\tif err = util.ValidateAIProviderHeaders(provider.Headers); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tai := &conf.AI{Providers: []*conf.Provider{provider}}\n\t\tai.Normalize()\n\t\tif len(ai.Providers) != 1 {\n\t\t\treturn nil, errors.New(\"invalid provider config\")\n\t\t}\n\t\treturn ai.Providers[0], nil\n\t}\n\n\tproviderID := request.Provider\n\tfor _, provider := range model.Conf.AI.Providers {\n\t\tif provider != nil && provider.ID == providerID {\n\t\t\treturn provider, nil\n\t\t}\n\t}\n\treturn nil, errors.New(\"provider not found\")\n}\n\nvar chatGPT = contractHandler(apicontract.AIChatGPT, chatGPTContract)\n\nfunc chatGPTContract(c *gin.Context, req apicontract.AIMessageRequest) apicontract.Response[string] {\n\n\treturn apicontract.Success(model.ChatGPT(req.Msg))","sourceCodeStart":59,"sourceCodeEnd":95,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/api/ai.go#L59-L95","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: wrapper object unmarshals to zero providers\n{\"providerConfig\": {\"provider\": {\"baseURL\": \"https://api.openai.com\"}}}\n// after: flat provider object\n{\"providerConfig\": {\"baseURL\": \"https://api.openai.com\", \"key\": \"sk-...\"}}","handlingStrategy":"validation","validationCode":"ai := &conf.AI{Providers: []*conf.Provider{provider}}\nai.Normalize()\nif len(ai.Providers) != 1 {\n    return errors.New(\"invalid provider config\")\n}","typeGuard":null,"tryCatchPattern":"provider, err := resolveAIProvider(req)\nif err != nil {\n    return contractFailure[string](gulu.Ret.NewResultWithMsg(err.Error()))\n}","preventionTips":["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"],"tags":["ai","config","api","normalization"],"backgroundTag":"invalid-config-value","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}