{"record":{"id":"96290260c2b56665","repo":"gastownhall/beads","slug":"metrics-resolve-data-dir-w","errorCode":null,"errorMessage":"metrics: resolve data dir: %w","messagePattern":"metrics: resolve data dir: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/metrics/metrics.go","lineNumber":67,"sourceCode":"}\n\nfunc Init(version string, enable bool, metricsEndpoint string) (func(context.Context), error) {\n\tenabled = enable\n\tendpoint = metricsEndpoint\n\tif endpoint == \"\" {\n\t\tendpoint = DefaultEndpoint\n\t}\n\n\tvar emitter eventkit.Emitter = eventkit.NullEmitter{}\n\t// The distinct ID is resolved only on the enabled path: computing it can\n\t// fork a platform probe (see cachedMachineID), and a disabled collector\n\t// never emits an event that would carry it. The placeholder below is inert\n\t// — NullEmitter drops everything and WithDisabled gates emission anyway.\n\tdistinctID := \"disabled\"\n\tif enabled {\n\t\tdir, err := DataDir()\n\t\tif err != nil {\n\t\t\treturn func(context.Context) {}, fmt.Errorf(\"metrics: resolve data dir: %w\", err)\n\t\t}\n\t\tfe, err := eventkit.NewFileEmitter(dir)\n\t\tif err != nil {\n\t\t\treturn func(context.Context) {}, fmt.Errorf(\"metrics: file emitter: %w\", err)\n\t\t}\n\t\temitter = fe\n\t\tdistinctID = cachedMachineID(AppName)\n\t}\n\n\tc := eventkit.NewCollector(emitter,\n\t\teventkit.WithDistinctID(distinctID),\n\t\teventkit.WithAppName(AppName),\n\t\teventkit.WithAppVersion(version),\n\t\teventkit.WithDisabled(func() bool { return !enabled }),\n\t)\n\teventkit.SetGlobal(c)\n\n\treturn func(ctx context.Context) {","sourceCodeStart":49,"sourceCodeEnd":85,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/metrics/metrics.go#L49-L85","documentation":"metrics.Init resolves the user's data directory (DataDir) so a file-based event emitter can be created. If DataDir fails — the data dir cannot be determined or created — Init aborts with 'metrics: resolve data dir: ...'. Telemetry cannot be set up, and the returned no-op closure is returned alongside the error so shutdown stays safe.","triggerScenarios":"Calling metrics.Init with enabled=true while internal/metrics.DataDir fails: unwritable $HOME/$XDG_DATA_HOME, HOME unset, or the data-dir path creation erroring.","commonSituations":"Containers/CI running with HOME unset or read-only; sandboxed environments blocking directory creation; disk permissions changed under the data root.","solutions":["Ensure HOME (or the relevant XDG dir) is set and writable by the process.","Create or fix permissions on the data directory.","If telemetry is unwanted, explicitly disable metrics (enabled=false) so DataDir is never resolved."],"exampleFix":"// before\ncleanup, err := metrics.Init(ctx, true)\n// after\nif os.Getenv(\"HOME\") == \"\" {\n    cleanup, err = metrics.Init(ctx, false) // skip emitter setup\n} else {\n    cleanup, err = metrics.Init(ctx, true)\n}","handlingStrategy":"fallback","validationCode":"if enabled {\n    if dir, err := metrics.DataDir(); err != nil {\n        enabled = false // telemetry optional\n    }\n}","typeGuard":null,"tryCatchPattern":"cleanup, err := metrics.Init(ctx, enabled)\nif err != nil && strings.HasPrefix(err.Error(), \"metrics: resolve data dir\") {\n    cleanup = func(context.Context) {}\n    return nil // degrade gracefully, telemetry is non-essential\n}","preventionTips":["Guarantee HOME is set in containers/CI.","Pre-create the data directory with correct permissions.","Treat metrics failures as non-fatal and fall back to disabled telemetry."],"tags":["metrics","filesystem","data-dir","configuration"],"backgroundTag":"data-dir-resolution-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}