{"record":{"id":"c9140efe8655bc30","repo":"SigNoz/signoz","slug":"errcodeinvalidcollectorconfig","errorCode":"ErrCodeInvalidCollectorConfig","errorMessage":"failed to unmarshal collector config","messagePattern":"failed to unmarshal collector config","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"pkg/types/llmpricingruletypes/processorconfig.go","lineNumber":118,"sourceCode":"\t\t\tCacheWrite: aiobservabilitytypes.SignozGenAICostCacheWrite,\n\t\t\tTotal:      aiobservabilitytypes.SignozGenAITotalCost,\n\t\t},\n\t}\n}\n\n// GenerateCollectorConfigWithLLMPricingProcessor injects (or replaces) the signozllmpricing\n// processor block in the collector YAML with one built from the given rules.\nfunc GenerateCollectorConfigWithLLMPricingProcessor(\n\tcurrentConfYaml []byte,\n\trules []*LLMPricingRule,\n) ([]byte, error) {\n\tif len(bytes.TrimSpace(currentConfYaml)) == 0 {\n\t\treturn currentConfYaml, nil\n\t}\n\n\tvar collectorConf map[string]any\n\tif err := yaml.Unmarshal(currentConfYaml, &collectorConf); err != nil {\n\t\treturn nil, errors.Wrapf(err, errors.TypeInvalidInput, ErrCodeInvalidCollectorConfig, \"failed to unmarshal collector config\")\n\t}\n\t// rare but don't do anything in this case, also means it's just comments.\n\tif collectorConf == nil {\n\t\treturn currentConfYaml, nil\n\t}\n\n\tprocessors := map[string]any{}\n\tif existing, ok := collectorConf[\"processors\"]; ok && existing != nil {\n\t\tp, ok := existing.(map[string]any)\n\t\tif !ok {\n\t\t\treturn nil, errors.Newf(errors.TypeInvalidInput, ErrCodeInvalidCollectorConfig, \"collector config 'processors' must be a mapping, got %T\", existing)\n\t\t}\n\t\tprocessors = p\n\t}\n\n\tprocessors[ProcessorName] = buildProcessorConfig(rules)\n\tcollectorConf[\"processors\"] = processors\n","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/SigNoz/signoz/blob/5069bf80b08f1f00d7e014eccc09902f9871004f/pkg/types/llmpricingruletypes/processorconfig.go#L100-L136","documentation":"GenerateCollectorConfigWithLLMPricingProcessor takes the agent's current collector config as YAML, and if non-empty (after TrimSpace) tries to yaml.Unmarshal it into map[string]any before injecting the LLM pricing processor. Failure means the existing config is not valid YAML (or not a mapping), so it cannot be programmatically modified.","triggerScenarios":"Calling RecommendAgentConfig (or this function directly) when the passed currentConfYaml contains YAML syntax errors, tabs used for indentation, duplicate keys, or a top-level scalar instead of a mapping.","commonSituations":"Users hand-editing odigos/otel collector config with tab indentation; configs with anchors/aliases malformed after templating; passing a JSON-with-BOM or binary content; passing a config whose top level is a list.","solutions":["Validate the YAML first: yaml.Unmarshal into map[string]any in a scratch program or run yamllint/otelcol validate --config","Replace tabs with spaces and fix indentation of nested keys (processors:, service:)","If the config is empty or comments-only, pass an empty []byte — the function passes it through untouched","Regenerate the config from a known-good template rather than patching the broken one"],"exampleFix":"# before (tabs)\nprocessors:\n\t\tredaction:\n\n# after (spaces)\nprocessors:\n  redaction:","handlingStrategy":"validation","validationCode":"var m map[string]any\nif err := yaml.Unmarshal(currentConfYaml, &m); err != nil {\n    return fmt.Errorf(\"collector config is not valid YAML: %w\", err)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Lint collector configs (yamllint / otelcol validate) before feeding to config generators","Never use tabs in YAML","Pass empty []byte for empty configs so passthrough applies"],"tags":["yaml","otel-collector","config","llm-pricing"],"backgroundTag":"invalid-yaml-config","analyzedSha":"5069bf80b08f1f00d7e014eccc09902f9871004f","analyzedAt":"2026-08-28T06:22:12.824Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}