router-for-me/CLIProxyAPI · warning
failed to merge metadata: %w
Error message
failed to merge metadata: %w
What it means
misc.MergeMetadata failed while merging the storage struct with its injected Metadata map during save. MergeMetadata marshals the token storage to JSON to combine top-level fields with metadata, so failure means json.Marshal hit an unmarshalable value — almost impossible for the well-defined CodexTokenStorage fields, and only realistic if Metadata contains unsupported types (chan, func, complex) or a value with a broken MarshalJSON.
Source
Thrown at internal/auth/codex/token.go:67
// data in JSON format to the specified file path for persistent storage.
// It merges any injected metadata into the top-level JSON object.
//
// Parameters:
// - authFilePath: The full path where the token file should be saved
//
// Returns:
// - error: An error if the operation fails, nil otherwise
func (ts *CodexTokenStorage) SaveTokenToFile(authFilePath string) error {
misc.LogSavingCredentials(authFilePath)
ts.Type = "codex"
if err := os.MkdirAll(filepath.Dir(authFilePath), 0700); err != nil {
return fmt.Errorf("failed to create directory: %v", err)
}
// Merge metadata using helper
data, errMerge := misc.MergeMetadata(ts, ts.Metadata)
if errMerge != nil {
return fmt.Errorf("failed to merge metadata: %w", errMerge)
}
f, err := os.Create(authFilePath)
if err != nil {
return fmt.Errorf("failed to create token file: %w", err)
}
defer func() {
if errClose := f.Close(); errClose != nil {
log.Errorf("codex token storage: close token file error: %v", errClose)
}
}()
if err = json.NewEncoder(f).Encode(data); err != nil {
return fmt.Errorf("failed to write token to file: %w", err)
}
return nil
}
View on GitHub (pinned to 78f0c4079e)
Solutions
- Inspect ts.Metadata for non-JSON-serializable values (funcs, channels, unsupported floats) before saving.
- Pre-validate metadata yourself: `_, err := json.Marshal(meta)` and fix or drop offending entries.
- Keep Metadata as map[string]string or plain JSON types.
- If a custom type is needed, give it a correct MarshalJSON implementation.
Example fix
// before
ts.Metadata = map[string]any{"done": make(chan struct{})}
// after
ts.Metadata = map[string]any{"source": "cli", "login_at": time.Now().Format(time.RFC3339)} Defensive patterns
Strategy: validation
Validate before calling
// Validate metadata is JSON-safe before saving
if ts.Metadata != nil {
if _, err := json.Marshal(ts.Metadata); err != nil {
return fmt.Errorf("metadata not serializable: %w", err)
}
} Try / catch
if err := ts.SaveTokenToFile(path); err != nil {
if strings.Contains(err.Error(), "failed to merge metadata") {
// strip non-JSON metadata and retry the save
}
} Prevention
- Keep Metadata limited to string/number/bool values.
- Marshal-check injected metadata in one place before it reaches storage.
When it happens
Trigger: Caller set Metadata containing func/chan/map with non-string keys before SaveTokenToFile; a custom metadata type whose MarshalJSON errors; NaN/Inf float values in metadata.
Common situations: Integrations injecting rich metadata objects into the token storage; almost never occurs via the standard login flow, whose Metadata stays string-keyed and JSON-safe.
Related errors
- failed to merge metadata: %w
- failed to save refreshed auth: %w
- failed to parse response JSON: %w
- response JSON does not contain models array
- failed to write token to file: %w
AI-assisted analysis of router-for-me/CLIProxyAPI@78f0c4079e (2026-08-15).
Data as JSON: /api/errors/6e5efb319149dab8.
Report an issue: GitHub.