{"record":{"id":"c92ed611f8e01c0f","repo":"router-for-me/CLIProxyAPI","slug":"failed-to-merge-metadata-w-c92ed6","errorCode":null,"errorMessage":"failed to merge metadata: %w","messagePattern":"failed to merge metadata: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/auth/kimi/token.go","lineNumber":94,"sourceCode":"\t// ExpiresIn is the number of seconds until the device code expires.\n\tExpiresIn int `json:\"expires_in\"`\n\t// Interval is the minimum number of seconds to wait between polling requests.\n\tInterval int `json:\"interval\"`\n}\n\n// SaveTokenToFile serializes the Kimi token storage to a JSON file.\nfunc (ts *KimiTokenStorage) SaveTokenToFile(authFilePath string) error {\n\tmisc.LogSavingCredentials(authFilePath)\n\tts.Type = \"kimi\"\n\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(\"kimi token storage: close token file error: %v\", errClose)\n\t\t}\n\t}()\n\n\tencoder := json.NewEncoder(f)\n\tencoder.SetIndent(\"\", \"  \")\n\tif err = encoder.Encode(data); err != nil {\n\t\treturn fmt.Errorf(\"failed to write token to file: %w\", err)\n\t}\n\treturn nil","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/auth/kimi/token.go#L76-L112","documentation":"Returned when misc.MergeMetadata fails while serializing the Kimi token storage together with its Metadata map before writing the JSON auth file (token.go:92-95). MergeMetadata merges the struct and its metadata into a single JSON-serializable value, so failure almost always means the Metadata map contains a value type that json.Marshal cannot encode (e.g. channels, funcs, cycles).","triggerScenarios":"KimiTokenStorage.Metadata populated with non-serializable values (func, chan, complex, or cyclic pointer structures) before SaveTokenToFile is called; extremely large metadata causing a marshaler panic captured as an error.","commonSituations":"Custom code embedding the SDK (sdk/cliproxy) that stuffs arbitrary Go values into Metadata; upstream code attaching an unserializable context-like object; virtually never happens with CLIProxyAPI's own metadata usage.","solutions":["Inspect what keys/values are placed into KimiTokenStorage.Metadata and restrict them to plain JSON types (string, number, bool, nested maps/slices)","If embedding the SDK, JSON-encode exotic values to strings before storing them as metadata","Reproduce with a minimal SaveTokenToFile call and check the wrapped %w cause for the exact marshal error"],"exampleFix":"// before\nts.Metadata = map[string]any{\"done\": make(chan struct{})}\n// after\nts.Metadata = map[string]any{\"done\": \"closed\"}","handlingStrategy":"validation","validationCode":"for k, v := range ts.Metadata {\n    if !isJSONScalarOrContainer(v) {\n        return fmt.Errorf(\"metadata key %q holds a non-serializable value\", k)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := ts.SaveTokenToFile(path); err != nil && strings.Contains(err.Error(), \"failed to merge metadata\") {\n    log.Errorf(\"unserializable kimi metadata; dropping metadata and retrying\")\n    ts.Metadata = nil\n    err = ts.SaveTokenToFile(path)\n}","preventionTips":["Only store JSON-native values in Metadata","Stringify custom objects before assigning them as metadata","Unit-test SaveTokenToFile with the exact metadata you plan to attach"],"tags":["json","kimi","auth","metadata"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}