{"record":{"id":"584312f5bf3a0a8c","repo":"vxcontrol/pentagi","slug":"failed-to-load-default-templates-w","errorCode":null,"errorMessage":"failed to load default templates: %w","messagePattern":"failed to load default templates: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"backend/pkg/controller/prompter.go","lineNumber":24,"sourceCode":"\n\t\"pentagi/pkg/database\"\n\t\"pentagi/pkg/templates\"\n)\n\n// newUserPrompter loads the user's custom prompts from the database and\n// overlays them onto the compiled default templates. Prompt types that\n// the user has not customized continue to use the defaults. A database\n// error is returned to the caller so that session creation fails\n// explicitly instead of silently falling back to defaults.\nfunc newUserPrompter(ctx context.Context, db database.Querier, userID int64) (templates.Prompter, error) {\n\tuserPrompts, err := db.GetUserPrompts(ctx, userID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load user prompts: %w\", err)\n\t}\n\n\tdefaults, err := templates.LoadDefaultPromptsMap()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to load default templates: %w\", err)\n\t}\n\n\treturn buildUserPrompter(defaults, userPrompts), nil\n}\n\n// buildUserPrompter is the pure merge step extracted from newUserPrompter so\n// it can be unit-tested without a database fake or filesystem access. It\n// mutates the supplied defaults map by overlaying each non-empty user\n// override on top, then returns a Prompter backed by that map. Callers must\n// pass a fresh map (e.g., from templates.LoadDefaultPromptsMap) so the\n// embedded defaults are not modified.\nfunc buildUserPrompter(defaults templates.PromptsMap, userPrompts []database.Prompt) templates.Prompter {\n\tfor _, p := range userPrompts {\n\t\tif p.Prompt == \"\" {\n\t\t\t// The Prompts UI uses delete (or reset, which writes the\n\t\t\t// default body back) to remove a customization, so an empty\n\t\t\t// body is unexpected. Skip it instead of clobbering the\n\t\t\t// default with an empty string that would later surface as","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/prompter.go#L6-L42","documentation":"newUserPrompter calls templates.LoadDefaultPromptsMap() to read the default prompt templates from the embedded filesystem (go:embed \"prompts\"). Failure is wrapped as \"failed to load default templates: %w\". Under the hood the templates are read once (sync.Once) from the embedded FS; errors come from ReadDir/ReadFile on the embedded \"prompts\" directory and are cached, so once it fails it keeps failing for the process lifetime.","triggerScenarios":"Creating or loading any flow/assistant worker when the embedded template FS read fails — realistically only a broken build/embed directive (prompts directory missing from the binary), since the templates are compiled in and not read from disk at runtime.","commonSituations":"Custom builds that removed or renamed the pkg/templates/prompts directory; build tooling that stripped embedded files; tampered or malformed binary after post-build processing (e.g. aggressive packing/upx or resource-stripping).","solutions":["Rebuild the backend binary from an intact source tree so the prompts directory is embedded correctly.","Verify backend/pkg/templates/prompts exists and contains *.tmpl files, and that the go:embed directive matches.","Note the error is cached in sync.Once — a restart will NOT clear it; only a correct binary will.","If running a custom build pipeline, ensure it does not strip or mutate embedded assets."],"exampleFix":"// before: prompts dir missing from the embed\n//go:embed prompts/*.tmpl\n\n// after: ensure files exist and embed whole directory\n//go:embed all:prompts","handlingStrategy":"validation","validationCode":"// verify templates load before starting workers (run once at startup)\nif _, err := templates.LoadDefaultPromptsMap(); err != nil {\n    log.Fatalf(\"embedded prompt templates broken: %v\", err)\n}","typeGuard":"null","tryCatchPattern":"prompter, err := newUserPrompter(ctx, db, userID)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to load default templates\") {\n        // cached in sync.Once — retrying in-process will NOT help; fail fast\n        return fmt.Errorf(\"binary is missing embedded templates, rebuild required: %w\", err)\n    }\n    return err\n}","preventionTips":["Smoke-test template loading at process startup, not on first worker creation.","Never strip embedded assets in build/packaging pipelines (upx, resource strippers).","Keep backend/pkg/templates/prompts and the go:embed directive intact.","Remember sync.Once caches the failure — restarts don't fix a broken binary, only a rebuild does."],"tags":["go","embedded-files","templates","build"],"backgroundTag":"embedded-template-load-failed","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}