{"record":{"id":"2d3f5c10a6ef4dcf","repo":"ytti/oxidized","slug":"hook-remote-repo-is-required","errorCode":null,"errorMessage":"hook.remote_repo is required","messagePattern":"hook\\.remote_repo is required","errorType":"validation","errorClass":"KeyError","httpStatus":null,"severity":"error","filePath":"lib/oxidized/hook/githubrepo.rb","lineNumber":5,"sourceCode":"require 'rugged'\n\nclass GithubRepo < Oxidized::Hook\n  def validate_cfg!\n    raise KeyError, 'hook.remote_repo is required' unless cfg.has_key?('remote_repo')\n  end\n\n  def run_hook(ctx)\n    unless ctx.node\n      logger.error 'GithubRepo.run_hook: no node provided'\n      return\n    end\n\n    unless ctx.node.repo\n      logger.error \"Oxidized output is not git, can't push to remote\"\n      return\n    end\n    repo  = Rugged::Repository.new(ctx.node.repo)\n    creds = credentials(ctx.node)\n    url   = remote_repo(ctx.node)\n\n    if url.nil? || url.empty?\n      logger.error \"No repository defined for #{ctx.node.group}/#{ctx.node.name}\"","sourceCodeStart":1,"sourceCodeEnd":23,"githubUrl":"https://github.com/ytti/oxidized/blob/687ed4262d9d21e54662e872e2383386d8498f7b/lib/oxidized/hook/githubrepo.rb#L1-L23","documentation":"The githubrepo hook validates at config-load time that its block contains a remote_repo key; KeyError 'hook.remote_repo is required' (lib/oxidized/hook/githubrepo.rb:5) aborts oxidized startup until the key exists. remote_repo is the git URL the hook pushes node configs to and may be a single URL string or a per-group mapping of repositories.","triggerScenarios":"Declaring a hook with type: githubrepo whose YAML block lacks remote_repo: (for example only username/password were filled in), or remote_repo present but indented under a sibling key so it is not part of the hook's cfg hash.","commonSituations":"Copy-pasting the githubrepo example and filling in only credentials; renaming remote_repo to repo or url; indentation mistakes after editing the hooks: section.","solutions":["Add the key under the hook block: remote_repo: git@github.com:corp/netconfigs.git","For per-group repos use a map: remote_repo: { default: git@github.com:corp/default.git, edge: git@github.com:corp/edge.git }","Verify remote_repo sits at the same indentation level as the hook's other keys","Restart oxidized; validation runs at load time and startup should proceed past hooks"],"exampleFix":"# before\nhooks:\n  push_to_github:\n    type: githubrepo\n    username: oxidized\n    password: secret\n\n# after\nhooks:\n  push_to_github:\n    type: githubrepo\n    remote_repo: git@github.com:corp/netconfigs.git\n    username: oxidized\n    password: secret","handlingStrategy":"validation","validationCode":"# check the githubrepo hook block before oxidized loads it\nhook_cfg = YAML.load_file('/etc/oxidized/config')['hooks']['push_to_github']\nmissing = %w[remote_repo].reject { |k| hook_cfg.key?(k) }\nraise ArgumentError, \"githubrepo config missing: #{missing.join(', ')}\" unless missing.empty?","typeGuard":"def githubrepo_cfg_valid?(cfg)\n  cfg.is_a?(Hash) &&\n    (cfg['remote_repo'].is_a?(String) || (cfg['remote_repo'].is_a?(Hash) && cfg['remote_repo'].any?))\nend","tryCatchPattern":"begin\n  hook.validate_cfg!\nrescue KeyError => e\n  abort \"fix the hooks config: #{e.message}\"\nend","preventionTips":["Keep a per-hook-type config template listing every required key","Deploy hook config through a pipeline that checks required keys before restart","After editing hooks, start oxidized once in the foreground to surface validation errors"],"tags":["oxidized","githubrepo-hook","config-validation","keyerror","git"],"backgroundTag":"missing-config-key","analyzedSha":"687ed4262d9d21e54662e872e2383386d8498f7b","analyzedAt":"2026-08-23T11:19:42.084Z","schemaVersion":2},"datasetVersion":"2026-08-23T16:17:53.355Z"}