router-for-me/CLIProxyAPI · error

pluginhost: command-line auth %d is invalid

Error message

pluginhost: command-line auth %d is invalid

What it means

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.

Source

Thrown at internal/pluginhost/command_line.go:383

func (h *Host) persistCommandLineAuths(ctx context.Context, auths []pluginapi.AuthData) ([]string, error) {
	if len(auths) == 0 {
		return nil, nil
	}
	store := sdkAuth.GetTokenStore()
	if store == nil {
		return nil, fmt.Errorf("pluginhost: token store unavailable")
	}
	summary := h.hostConfigSummary()
	if summary.AuthDir != "" {
		if setter, okSetter := store.(interface{ SetBaseDir(string) }); okSetter {
			setter.SetBaseDir(summary.AuthDir)
		}
	}
	savedPaths := make([]string, 0, len(auths))
	for index, authData := range auths {
		record := h.AuthDataToCoreAuth(authData, "", "")
		if record == nil {
			return savedPaths, fmt.Errorf("pluginhost: command-line auth %d is invalid", index+1)
		}
		savedPath, errSave := store.Save(ctx, record)
		if errSave != nil {
			return savedPaths, fmt.Errorf("pluginhost: save command-line auth %s: %w", record.ID, errSave)
		}
		if strings.TrimSpace(savedPath) != "" {
			savedPaths = append(savedPaths, savedPath)
		}
	}
	return savedPaths, nil
}

func appendCommandLineSavedPaths(stdout []byte, savedPaths []string) []byte {
	if len(savedPaths) == 0 {
		return stdout
	}
	out := append([]byte(nil), stdout...)
	if len(out) > 0 && out[len(out)-1] != '\n' {

View on GitHub (pinned to 78f0c4079e)

Solutions

  1. Identify which entry failed from the 1-based index in the message and dump the plugin's returned AuthData list for inspection.
  2. Fix the plugin to omit invalid entries or fail the command with a proper error rather than returning partial auth data.
  3. Check auths already persisted before the failing index — remove duplicates if you re-run the command after fixing.
  4. Align plugin and host on the same pluginapi version.

Example fix

// plugin side, before
return pluginapi.CommandLineResponse{
	Auths: []pluginapi.AuthData{{Provider: "myprov"}, validAuth}, // first entry lacks values
}, nil

// after
auths := []pluginapi.AuthData{}
if validAuth.Provider != "" {
	auths = append(auths, validAuth)
}
return pluginapi.CommandLineResponse{Auths: auths}, nil
Defensive patterns

Strategy: validation

Validate before calling

// Filter plugin-returned auths before persistence
for i, a := range auths {
    if strings.TrimSpace(a.Provider) == "" || len(a.StorageJSON) == 0 {
        return fmt.Errorf("plugin returned invalid auth at index %d", i+1)
    }
}

Type guard

func allAuthDataValid(auths []pluginapi.AuthData) bool {
    for _, a := range auths {
        if strings.TrimSpace(a.Provider) == "" {
            return false
        }
    }
    return true
}

Try / catch

paths, err := host.PersistCommandLineAuths(ctx, auths)
if err != nil {
    if strings.Contains(err.Error(), "auth %d is invalid") {
        // partial success: paths contains entries already saved; dedupe before re-run
        return cleanupPartial(paths, err)
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15). Data as JSON: /api/errors/13a804f5ba1e5d9b. Report an issue: GitHub.