{"record":{"id":"6e5efb319149dab8","repo":"router-for-me/CLIProxyAPI","slug":"failed-to-merge-metadata-w-6e5efb","errorCode":null,"errorMessage":"failed to merge metadata: %w","messagePattern":"failed to merge metadata: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/auth/codex/token.go","lineNumber":67,"sourceCode":"// data in JSON format to the specified file path for persistent storage.\n// It merges any injected metadata into the top-level JSON object.\n//\n// Parameters:\n//   - authFilePath: The full path where the token file should be saved\n//\n// Returns:\n//   - error: An error if the operation fails, nil otherwise\nfunc (ts *CodexTokenStorage) SaveTokenToFile(authFilePath string) error {\n\tmisc.LogSavingCredentials(authFilePath)\n\tts.Type = \"codex\"\n\tif err := os.MkdirAll(filepath.Dir(authFilePath), 0700); err != nil {\n\t\treturn fmt.Errorf(\"failed to create directory: %v\", err)\n\t}\n\n\t// Merge metadata using helper\n\tdata, errMerge := misc.MergeMetadata(ts, ts.Metadata)\n\tif errMerge != nil {\n\t\treturn fmt.Errorf(\"failed to merge metadata: %w\", errMerge)\n\t}\n\n\tf, err := os.Create(authFilePath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create token file: %w\", err)\n\t}\n\tdefer func() {\n\t\tif errClose := f.Close(); errClose != nil {\n\t\t\tlog.Errorf(\"codex token storage: close token file error: %v\", errClose)\n\t\t}\n\t}()\n\n\tif err = json.NewEncoder(f).Encode(data); err != nil {\n\t\treturn fmt.Errorf(\"failed to write token to file: %w\", err)\n\t}\n\treturn nil\n}\n","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/codex/token.go#L49-L85","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nts.Metadata = map[string]any{\"done\": make(chan struct{})}\n\n// after\nts.Metadata = map[string]any{\"source\": \"cli\", \"login_at\": time.Now().Format(time.RFC3339)}","handlingStrategy":"validation","validationCode":"// Validate metadata is JSON-safe before saving\nif ts.Metadata != nil {\n    if _, err := json.Marshal(ts.Metadata); err != nil {\n        return fmt.Errorf(\"metadata not serializable: %w\", err)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := ts.SaveTokenToFile(path); err != nil {\n    if strings.Contains(err.Error(), \"failed to merge metadata\") {\n        // strip non-JSON metadata and retry the save\n    }\n}","preventionTips":["Keep Metadata limited to string/number/bool values.","Marshal-check injected metadata in one place before it reaches storage."],"tags":["json","metadata","storage","codex"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}