{"record":{"id":"6e57cf47b936ba9d","repo":"chenhg5/cc-connect","slug":"max-token-is-required","errorCode":null,"errorMessage":"max: token is required","messagePattern":"max: token is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"platform/max/max.go","lineNumber":107,"sourceCode":"//\tapi_base       = \"https://platform-api.max.ru\"  # optional override\n//\twebhook_url    = \"https://your.domain/webhook\"  # optional; switches\n//\t                                               # platform to webhook mode\n//\twebhook_listen = \":8080\"                       # optional, default \":8080\"\n//\twebhook_path   = \"/webhook\"                    # optional, default \"/webhook\";\n//\t                                               # must match the path in webhook_url\n//\twebhook_secret = \"<random-string>\"             # optional; if set, sent to MAX\n//\t                                               # so MAX includes it in the\n//\t                                               # X-Max-Bot-Api-Secret header\n//\t                                               # of every webhook POST (?s= also\n//\t                                               # accepted for manual testing)\n//\twebhook_resubscribe_interval = \"5m\"            # optional, default 5m; cc-connect\n//\t                                               # periodically re-POSTs the\n//\t                                               # subscription because MAX has been\n//\t                                               # observed to silently drop it\nfunc New(opts map[string]any) (core.Platform, error) {\n\ttoken, _ := opts[\"token\"].(string)\n\tif token == \"\" {\n\t\treturn nil, fmt.Errorf(\"max: token is required\")\n\t}\n\tapiBase, _ := opts[\"api_base\"].(string)\n\tif apiBase == \"\" {\n\t\tapiBase = defaultAPIBase\n\t}\n\tallowFrom, _ := opts[\"allow_from\"].(string)\n\tcore.CheckAllowFrom(\"max\", allowFrom)\n\n\twebhookURL, _ := opts[\"webhook_url\"].(string)\n\twebhookListen, _ := opts[\"webhook_listen\"].(string)\n\twebhookPath, _ := opts[\"webhook_path\"].(string)\n\twebhookSecret, _ := opts[\"webhook_secret\"].(string)\n\tif webhookURL != \"\" && webhookListen == \"\" {\n\t\twebhookListen = \":8080\"\n\t}\n\tif webhookPath == \"\" {\n\t\twebhookPath = \"/webhook\"\n\t} else if !strings.HasPrefix(webhookPath, \"/\") {","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/platform/max/max.go#L89-L125","documentation":"The MAX platform's New() constructor requires a \"token\" string in its options map and fails with \"max: token is required\" when it is absent or empty. The token authenticates all MAX Bot API requests, so the platform cannot be constructed without one. This is a fail-fast validation at configuration load time.","triggerScenarios":"Creating the MAX platform with opts lacking opts[\"token\"] or with an empty string — e.g., the config.toml [platform.max] section has no token field, or the token value is an empty/unset variable interpolation.","commonSituations":"Fresh install where config.example.toml was copied but the token placeholder never filled in; environment-variable interpolation producing an empty string because MAX_BOT_TOKEN is unset; token accidentally placed in the wrong config section so the factory never sees it.","solutions":["Add token = \"<your MAX bot token>\" to the MAX platform section of config.toml","If using an env var, ensure it is exported in the daemon's environment and the interpolation resolves non-empty","Verify the token is in the correct [platform.max] table, not a sibling section","Obtain a bot token from the MAX bot platform if you don't have one"],"exampleFix":"# before (config.toml)\n[platform.max]\ntoken = \"\"\n\n# after\n[platform.max]\ntoken = \"your-max-bot-token\"","handlingStrategy":"validation","validationCode":"cfg := \"\"\nif v, ok := opts[\"token\"].(string); ok {\n    cfg = v\n}\nif cfg == \"\" {\n    // abort before calling New()\n}","typeGuard":"func tokenFromOpts(opts map[string]any) (string, bool) {\n    t, ok := opts[\"token\"].(string)\n    return t, ok && t != \"\"\n}","tryCatchPattern":"p, err := max.New(opts)\nif err != nil {\n    if strings.Contains(err.Error(), \"token is required\") {\n        return fmt.Errorf(\"config: set [platform.max] token in config.toml: %w\", err)\n    }\n    return err\n}","preventionTips":["Validate config.toml at startup before platform construction","Use cc-connect doctor / config validation to catch missing tokens","Never interpolate an unset env var into the token field silently — check it resolves non-empty"],"tags":["max","configuration","token","validation"],"backgroundTag":"missing-required-config-field","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"}