{"record":{"id":"13a804f5ba1e5d9b","repo":"router-for-me/CLIProxyAPI","slug":"pluginhost-command-line-auth-d-is-invalid","errorCode":null,"errorMessage":"pluginhost: command-line auth %d is invalid","messagePattern":"pluginhost: command-line auth (.+?) is invalid","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/pluginhost/command_line.go","lineNumber":383,"sourceCode":"func (h *Host) persistCommandLineAuths(ctx context.Context, auths []pluginapi.AuthData) ([]string, error) {\n\tif len(auths) == 0 {\n\t\treturn nil, nil\n\t}\n\tstore := sdkAuth.GetTokenStore()\n\tif store == nil {\n\t\treturn nil, fmt.Errorf(\"pluginhost: token store unavailable\")\n\t}\n\tsummary := h.hostConfigSummary()\n\tif summary.AuthDir != \"\" {\n\t\tif setter, okSetter := store.(interface{ SetBaseDir(string) }); okSetter {\n\t\t\tsetter.SetBaseDir(summary.AuthDir)\n\t\t}\n\t}\n\tsavedPaths := make([]string, 0, len(auths))\n\tfor index, authData := range auths {\n\t\trecord := h.AuthDataToCoreAuth(authData, \"\", \"\")\n\t\tif record == nil {\n\t\t\treturn savedPaths, fmt.Errorf(\"pluginhost: command-line auth %d is invalid\", index+1)\n\t\t}\n\t\tsavedPath, errSave := store.Save(ctx, record)\n\t\tif errSave != nil {\n\t\t\treturn savedPaths, fmt.Errorf(\"pluginhost: save command-line auth %s: %w\", record.ID, errSave)\n\t\t}\n\t\tif strings.TrimSpace(savedPath) != \"\" {\n\t\t\tsavedPaths = append(savedPaths, savedPath)\n\t\t}\n\t}\n\treturn savedPaths, nil\n}\n\nfunc appendCommandLineSavedPaths(stdout []byte, savedPaths []string) []byte {\n\tif len(savedPaths) == 0 {\n\t\treturn stdout\n\t}\n\tout := append([]byte(nil), stdout...)\n\tif len(out) > 0 && out[len(out)-1] != '\\n' {","sourceCodeStart":365,"sourceCodeEnd":401,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/pluginhost/command_line.go#L365-L401","documentation":"While persisting auths returned by a plugin command-line run, each pluginapi.AuthData is converted with AuthDataToCoreAuth; a nil result means that auth entry (1-based index in the message) is invalid — missing provider, unusable type, or empty value payload — and the whole persistence batch aborts, returning the paths saved so far plus this error.","triggerScenarios":"A plugin's command-line response includes an AuthData entry with an empty provider ID, unsupported auth type, or no credential value. Any earlier valid entries are already saved (partial success) when the error returns.","commonSituations":"Plugin emits placeholder/empty auth entries when login fails midway instead of returning an error; schema drift between plugin and pluginapi versions; plugin returns multiple auths where one is incompletely populated.","solutions":["Identify which entry failed from the 1-based index in the message and dump the plugin's returned AuthData list for inspection.","Fix the plugin to omit invalid entries or fail the command with a proper error rather than returning partial auth data.","Check auths already persisted before the failing index — remove duplicates if you re-run the command after fixing.","Align plugin and host on the same pluginapi version."],"exampleFix":"// plugin side, before\nreturn pluginapi.CommandLineResponse{\n\tAuths: []pluginapi.AuthData{{Provider: \"myprov\"}, validAuth}, // first entry lacks values\n}, nil\n\n// after\nauths := []pluginapi.AuthData{}\nif validAuth.Provider != \"\" {\n\tauths = append(auths, validAuth)\n}\nreturn pluginapi.CommandLineResponse{Auths: auths}, nil","handlingStrategy":"validation","validationCode":"// Filter plugin-returned auths before persistence\nfor i, a := range auths {\n    if strings.TrimSpace(a.Provider) == \"\" || len(a.StorageJSON) == 0 {\n        return fmt.Errorf(\"plugin returned invalid auth at index %d\", i+1)\n    }\n}","typeGuard":"func allAuthDataValid(auths []pluginapi.AuthData) bool {\n    for _, a := range auths {\n        if strings.TrimSpace(a.Provider) == \"\" {\n            return false\n        }\n    }\n    return true\n}","tryCatchPattern":"paths, err := host.PersistCommandLineAuths(ctx, auths)\nif err != nil {\n    if strings.Contains(err.Error(), \"auth %d is invalid\") {\n        // partial success: paths contains entries already saved; dedupe before re-run\n        return cleanupPartial(paths, err)\n    }\n    return err\n}","preventionTips":["Plugins should validate their own AuthData before returning it.","After a partial-save failure, reconcile saved paths against the store to avoid duplicates."],"tags":["plugin","auth","validation","command-line","pluginhost"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}