{"record":{"id":"641278543cfd7b55","repo":"gastownhall/beads","slug":"reading-source-content-for-s-from-s-w","errorCode":null,"errorMessage":"reading source content for %s from %s: %w","messagePattern":"reading source content for (.+?) from (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/migrate_hooks_apply.go","lineNumber":316,"sourceCode":"\t\t\tHookName: op.HookName,\n\t\t\tPath:     op.HookPath,\n\t\t\tContent:  rendered,\n\t\t})\n\t}\n\n\treturn prepared, nil\n}\n\nfunc renderMigratedHookContent(op hookMigrationWriteOp) ([]byte, error) {\n\tvar baseContent string\n\n\tswitch op.SourceKind {\n\tcase hookMigrationWriteFromTemplate:\n\t\tbaseContent = \"\"\n\tcase hookMigrationWriteFromHookFile, hookMigrationWriteFromOld, hookMigrationWriteFromBackup:\n\t\tcontent, err := os.ReadFile(op.SourcePath) // #nosec G304 -- source paths come from migration planner + known sidecar suffixes\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"reading source content for %s from %s: %w\", op.HookName, op.SourcePath, err)\n\t\t}\n\t\tbaseContent = string(content)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown source kind %q for %s\", op.SourceKind, op.HookName)\n\t}\n\n\tbaseContent = strings.ReplaceAll(baseContent, \"\\r\\n\", \"\\n\")\n\tbaseContent = ensureHookShebang(baseContent)\n\n\tcontent := injectHookSection(baseContent, generateHookSection(op.HookName))\n\tcontent = strings.ReplaceAll(content, \"\\r\\n\", \"\\n\")\n\tif !strings.HasSuffix(content, \"\\n\") {\n\t\tcontent += \"\\n\"\n\t}\n\n\treturn []byte(content), nil\n}\n","sourceCodeStart":298,"sourceCodeEnd":334,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/migrate_hooks_apply.go#L298-L334","documentation":"renderMigratedHookContent builds the new hook body from a base source: for hook-file/old/backup source kinds it reads the existing file at op.SourcePath. This error wraps any os.ReadFile failure (missing file, permissions) with the hook name and source path so the user knows which sidecar could not be read.","triggerScenarios":"prepareHookMigrationPlanning chose hookMigrationWriteFromHookFile/Old/Backup, but the referenced file does not exist or is unreadable when content is rendered.","commonSituations":"Sidecar file (.old/.backup) deleted by the user or a cleanup tool between plan and apply; stale plan referencing a sidecar that no longer exists; permission-restricted hook files.","solutions":["Verify the source file exists: `ls -la` the path named in the error","Restore the sidecar from backup/SCM, or re-run the migration plan so a fresh source is chosen","If the sidecar is intentionally gone, delete it from the plan and migrate from template instead","Fix read permissions on the source file"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// confirm sidecar sources exist before apply\nfor (const op of plan.RetireOps.concat(plan.WriteOps)) {\n  if (op.SourceKind !== 'template' && !fs.existsSync(op.SourcePath)) throw new Error('missing source: '+op.SourcePath);\n}","typeGuard":null,"tryCatchPattern":"if err := apply(...); err != nil && strings.Contains(err.Error(), \"reading source content for\") {\n    // restore the sidecar or regenerate the plan, then retry\n}","preventionTips":["Don't delete .old/.backup sidecars between plan and apply","Always regenerate the plan after any manual hook-directory changes","Back up hooks directory before migrating"],"tags":["filesystem","git-hooks","migration","missing-file"],"backgroundTag":"source-file-missing","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}