{"record":{"id":"a4422ef79f6524cb","repo":"chenhg5/cc-connect","slug":"bot-token-is-required-format-app-key-app-secret","errorCode":null,"errorMessage":"bot_token is required (format: app_key:app_secret)","messagePattern":"bot_token is required \\(format: app_key:app_secret\\)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"config/config.go","lineNumber":2707,"sourceCode":"\tAllowFrom        string\n}\n\n// SaveYuanbaoPlatformCredentials updates bot_token (and optional fields)\n// for a project's Yuanbao platform.\nfunc SaveYuanbaoPlatformCredentials(opts YuanbaoCredentialUpdateOptions) (*YuanbaoCredentialUpdateResult, error) {\n\tconfigMu.Lock()\n\tdefer configMu.Unlock()\n\n\tif ConfigPath == \"\" {\n\t\treturn nil, fmt.Errorf(\"config path not set\")\n\t}\n\tif strings.TrimSpace(opts.ProjectName) == \"\" {\n\t\treturn nil, fmt.Errorf(\"project name is required\")\n\t}\n\tbotToken := strings.TrimSpace(opts.BotToken)\n\tidx := strings.Index(botToken, \":\")\n\tif botToken == \"\" || idx <= 0 || idx >= len(botToken)-1 {\n\t\treturn nil, fmt.Errorf(\"bot_token is required (format: app_key:app_secret)\")\n\t}\n\tif opts.PlatformIndex < 0 {\n\t\treturn nil, fmt.Errorf(\"platform index must be >= 0\")\n\t}\n\n\tdata, err := os.ReadFile(ConfigPath)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read config: %w\", err)\n\t}\n\traw := string(data)\n\tcfg := &Config{}\n\tif err := toml.Unmarshal(data, cfg); err != nil {\n\t\treturn nil, fmt.Errorf(\"parse config: %w\", err)\n\t}\n\n\tprojectIdx := -1\n\tfor i := range cfg.Projects {\n\t\tif cfg.Projects[i].Name == opts.ProjectName {","sourceCodeStart":2689,"sourceCodeEnd":2725,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/config/config.go#L2689-L2725","documentation":"Returned when opts.BotToken is empty or does not contain a colon in a valid app_key:app_secret position (colon at index 0 or at the very end). The yuanbao platform authenticates via a combined token string, and this check enforces that format before touching the config file.","triggerScenarios":"Calling the API with BotToken left empty, BotToken set to just \"app_key\" (no colon), \":app_secret\", or a token made of only whitespace after trimming.","commonSituations":"Copying only the app key into config; shell quoting dropping the secret part of the token; an env var like YUANBAO_BOT_TOKEN unset so the value interpolates to empty; using a full URL or JWT instead of the app_key:app_secret pair.","solutions":["Provide the token as `app_key:app_secret` with both parts non-empty, e.g. opts.BotToken = \"mykey:mysecret\"","Check the env var or secret store actually contains the colon-separated value: echo \"$YUANBAO_BOT_TOKEN\" | grep ':'","Regenerate/re-copy credentials from the yuanbao console if the secret half is missing"],"exampleFix":"// before\nopts.BotToken = os.Getenv(\"YUANBAO_KEY\") // only app key, no colon\n// after\nopts.BotToken = os.Getenv(\"YUANBAO_KEY\") + \":\" + os.Getenv(\"YUANBAO_SECRET\")","handlingStrategy":"validation","validationCode":"tok := strings.TrimSpace(opts.BotToken)\nif i := strings.Index(tok, \":\"); tok == \"\" || i <= 0 || i >= len(tok)-1 {\n    return fmt.Errorf(\"bot_token must be app_key:app_secret\")\n}","typeGuard":"func validBotToken(tok string) bool {\n    tok = strings.TrimSpace(tok)\n    i := strings.Index(tok, \":\")\n    return tok != \"\" && i > 0 && i < len(tok)-1\n}","tryCatchPattern":"if err != nil {\n    if strings.Contains(err.Error(), \"bot_token is required\") {\n        slog.Error(\"check YUANBAO_BOT_TOKEN format: expected app_key:app_secret\")\n        return err\n    }\n    return err\n}","preventionTips":["Store app_key and app_secret separately and join them programmatically","Never pass the token through shell interpolation that can drop the secret half","Validate credentials once at startup"],"tags":["config","credentials","validation"],"backgroundTag":"missing-credentials","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}