{"record":{"id":"7095d6ecd5d2a674","repo":"Tencent/WeKnora","slug":"parse-yuque-credentials-w","errorCode":null,"errorMessage":"parse yuque credentials: %w","messagePattern":"parse yuque credentials: %w","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/datasource/connector/yuque/types.go","lineNumber":75,"sourceCode":"\turl = strings.TrimRight(url, \"/\")\n\treturn url\n}\n\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.","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/datasource/connector/yuque/types.go#L57-L93","documentation":"After marshalling, parseYuqueConfig unmarshals the JSON into the typed Config struct. This error wraps any json.Unmarshal failure, meaning the credentials map's shape/types do not match the Config struct (e.g. api_token is a number or object instead of a string).","triggerScenarios":"config.Credentials contains fields whose JSON types are incompatible with Config (e.g. api_token as integer, or a nested object where a string is expected).","commonSituations":"Hand-edited YAML/JSON config where the token was quoted incorrectly; config migrations changing credential field types; API/stored credentials with unexpected types.","solutions":["Ensure every credential field is the type Config expects — api_token must be a JSON string: quote it in the config file.","Compare the credentials keys/types against the yuque Config struct definition.","If the raw error mentions 'cannot unmarshal number into Go struct field', fix that specific field's type."],"exampleFix":"// before (config.yml)\napi_token: 12345678\n// after\napi_token: \"12345678\"","handlingStrategy":"validation","validationCode":"var probe struct {\n    APIToken string `json:\"api_token\"`\n}\nb, _ := json.Marshal(config.Credentials)\nif err := json.Unmarshal(b, &probe); err != nil {\n    return fmt.Errorf(\"credential types invalid: %w\", err)\n}","typeGuard":"func validYuqueCreds(creds map[string]any) bool {\n    t, ok := creds[\"api_token\"]\n    return ok && reflect.TypeOf(t).Kind() == reflect.String\n}","tryCatchPattern":"cfg, err := parseYuqueConfig(config)\nif err != nil {\n    var typeErr *json.UnmarshalTypeError\n    if errors.As(err, &typeErr) {\n        return fmt.Errorf(\"field %s has wrong type\", typeErr.Field)\n    }\n    return err\n}","preventionTips":["Quote all token values in YAML/JSON config files.","Keep credential field types consistent across config migrations.","Round-trip credentials through the Config struct in tests."],"tags":["json","unmarshal","credentials"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}