{"record":{"id":"d4870c53418c92ba","repo":"gastownhall/beads","slug":"formula-has-no-source-path","errorCode":null,"errorMessage":"formula has no source path","messagePattern":"formula has no source path","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/formula.go","lineNumber":669,"sourceCode":"\n// findFormulaJSON searches for a JSON formula file by name.\nfunc findFormulaJSON(name string) string {\n\tfor _, dir := range getFormulaSearchPaths() {\n\t\tpath := filepath.Join(dir, name+formula.FormulaExtJSON)\n\t\tif _, err := os.Stat(path); err == nil {\n\t\t\treturn path\n\t\t}\n\t}\n\treturn \"\"\n}\n\n// formulaToTOML converts a Formula to TOML bytes.\n// Uses a custom structure optimized for TOML readability.\nfunc formulaToTOML(f *formula.Formula) ([]byte, error) {\n\t// We need to re-read the original JSON to get the raw structure\n\t// because the Formula struct loses some ordering/formatting\n\tif f.Source == \"\" {\n\t\treturn nil, fmt.Errorf(\"formula has no source path\")\n\t}\n\n\t// Read the original JSON\n\tjsonData, err := os.ReadFile(f.Source)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"reading source: %w\", err)\n\t}\n\n\t// Parse into a map to preserve structure\n\tvar raw map[string]interface{}\n\tif err := json.Unmarshal(jsonData, &raw); err != nil {\n\t\treturn nil, fmt.Errorf(\"parsing JSON: %w\", err)\n\t}\n\n\t// Fix float64 to int for known integer fields\n\tfixIntegerFields(raw)\n\n\t// Encode to TOML","sourceCodeStart":651,"sourceCodeEnd":687,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/formula.go#L651-L687","documentation":"formulaToTOML in cmd/bd/formula.go converts a Formula back to TOML by re-reading the original JSON source file, because the parsed Formula struct loses ordering and formatting. If the Formula's Source field (path to the original JSON) is empty, the conversion cannot proceed and this error is thrown. It is reached from `bd formula convert` (runFormulaConvert) and bulk conversion (convertAllFormulas).","triggerScenarios":"Running formula conversion on a Formula instance whose Source field was never populated — e.g. a formula constructed in memory rather than loaded from a JSON file, or a formula loaded through a path that did not record its source.","commonSituations":"Programmatically built formulas passed to conversion; formulas loaded from an embedded or non-file store that don't set Source; older formula JSON files or loaders predating the Source field; converting a formula after the source file reference was cleared.","solutions":["Load the formula from its JSON file via the normal loader so Source is populated before converting.","Set f.Source to the original JSON file path before calling formulaToTOML.","If the original JSON no longer exists, recreate/export the formula JSON first, then convert.","For bulk conversion, ensure every formula in the directory has a recorded source path."],"exampleFix":"// before\nf := &formula.Formula{Name: \"x\"}\ntomlBytes, err := formulaToTOML(f) // \"formula has no source path\"\n// after\nf.Source = \".beads/formulas/x.json\"\ntomlBytes, err := formulaToTOML(f)","handlingStrategy":"validation","validationCode":"// before converting, ensure Source is set\nif f.Source == \"\" {\n    return fmt.Errorf(\"cannot convert formula %q: no source JSON path\", f.Name)\n}","typeGuard":"// Go\nfunc convertible(f *formula.Formula) bool { return f != nil && f.Source != \"\" }","tryCatchPattern":null,"preventionTips":["Always load formulas through the file-based loader so Source is populated.","Never construct Formula values by hand for conversion.","Round-trip through export before converting programmatically built formulas.","In bulk conversion, skip/log formulas with empty Source explicitly."],"tags":["formula","conversion","toml"],"backgroundTag":"missing-source-path","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}