{"record":{"id":"2b73f20150b49244","repo":"Tencent/WeKnora","slug":"errinvalidcredentials-2b73f2","errorCode":"ErrInvalidCredentials","errorMessage":"%w: api_token is required","messagePattern":"%w: api_token is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/datasource/connector/yuque/types.go","lineNumber":78,"sourceCode":"\n// parseYuqueConfig extracts and validates Yuque-specific configuration.\n// Uses JSON marshal/unmarshal roundtrip (consistent with Feishu's parseFeishuConfig)\n// rather than single-field type assertion, because we have multiple fields with\n// optional defaults.\nfunc parseYuqueConfig(config *types.DataSourceConfig) (*Config, error) {\n\tif config == nil {\n\t\treturn nil, fmt.Errorf(\"%w: config is nil\", datasource.ErrInvalidConfig)\n\t}\n\tcredBytes, err := json.Marshal(config.Credentials)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"marshal credentials: %w\", err)\n\t}\n\tvar cfg Config\n\tif err := json.Unmarshal(credBytes, &cfg); err != nil {\n\t\treturn nil, fmt.Errorf(\"parse yuque credentials: %w\", err)\n\t}\n\tif strings.TrimSpace(cfg.APIToken) == \"\" {\n\t\treturn nil, fmt.Errorf(\"%w: api_token is required\", datasource.ErrInvalidCredentials)\n\t}\n\tif err := datasource.ValidateConnectorBaseURL(cfg.GetBaseURL()); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &cfg, nil\n}\n\n// --- Yuque API response types ---\n\n// flexibleStatus accepts either a string (\"1\") or a number (1) for the doc\n// `status` field. Yuque's OpenAPI spec declares `status` as string, but the\n// runtime API returns it as an integer, so unmarshaling into a plain string\n// fails with \"cannot unmarshal number into Go struct field ... of type string\".\n// Normalizing to the textual form lets existing comparisons (e.g. != \"1\") keep\n// working for both response shapes.\ntype flexibleStatus string\n\nfunc (s *flexibleStatus) UnmarshalJSON(b []byte) error {","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/datasource/connector/yuque/types.go#L60-L96","documentation":"The Yuque connector requires a non-empty, non-whitespace api_token credential. parseYuqueConfig wraps datasource.ErrInvalidCredentials when cfg.APIToken is empty after trimming, so the connector cannot authenticate against the Yuque API.","triggerScenarios":"Credentials map exists but lacks the api_token key, or api_token is \"\" / whitespace-only.","commonSituations":"Datasource saved with placeholder credentials; env var holding the token unset so it interpolates to empty; token field renamed in a config update; fresh install where YUQUE_API_TOKEN was never set.","solutions":["Set api_token in the datasource credentials to a valid Yuque personal access token (generate at yuque.com settings > developer tokens).","If using env interpolation, verify the env variable is set and non-empty in the runtime environment.","Re-save the datasource after adding the token so the credentials map is persisted."],"exampleFix":"// before\n\"credentials\": {\"api_token\": \"\"}\n// after\n\"credentials\": {\"api_token\": \"<yuque-personal-access-token>\"}","handlingStrategy":"validation","validationCode":"if creds[\"api_token\"] == \"\" || strings.TrimSpace(creds[\"api_token\"].(string)) == \"\" {\n    return errors.New(\"yuque api_token must be set before validation\")\n}","typeGuard":"func hasAPIToken(cfg *Config) bool {\n    return cfg != nil && strings.TrimSpace(cfg.APIToken) != \"\"\n}","tryCatchPattern":"if _, err := parseYuqueConfig(config); err != nil {\n    if errors.Is(err, datasource.ErrInvalidCredentials) {\n        return errors.New(\"configure a Yuque API token in datasource settings\")\n    }\n    return err\n}","preventionTips":["Set the api_token credential when creating the datasource, not after.","Verify env-interpolated token variables are populated in the deployment environment.","Check errors.Is(err, datasource.ErrInvalidCredentials) to give users a targeted message."],"tags":["credentials","auth","yuque"],"backgroundTag":"missing-api-token","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}