gastownhall/beads · error

unknown source kind %q for %s

Error message

unknown source kind %q for %s

What it means

The SourceKind on a hook migration write op is a closed enum (template, hook file, old, backup). If renderMigratedHookContent sees any other value, it fails fast rather than guessing how to build content — this indicates an internal inconsistency between the planner and the renderer.

Source

Thrown at cmd/bd/migrate_hooks_apply.go:320

	}

	return prepared, nil
}

func renderMigratedHookContent(op hookMigrationWriteOp) ([]byte, error) {
	var baseContent string

	switch op.SourceKind {
	case hookMigrationWriteFromTemplate:
		baseContent = ""
	case hookMigrationWriteFromHookFile, hookMigrationWriteFromOld, hookMigrationWriteFromBackup:
		content, err := os.ReadFile(op.SourcePath) // #nosec G304 -- source paths come from migration planner + known sidecar suffixes
		if err != nil {
			return nil, fmt.Errorf("reading source content for %s from %s: %w", op.HookName, op.SourcePath, err)
		}
		baseContent = string(content)
	default:
		return nil, fmt.Errorf("unknown source kind %q for %s", op.SourceKind, op.HookName)
	}

	baseContent = strings.ReplaceAll(baseContent, "\r\n", "\n")
	baseContent = ensureHookShebang(baseContent)

	content := injectHookSection(baseContent, generateHookSection(op.HookName))
	content = strings.ReplaceAll(content, "\r\n", "\n")
	if !strings.HasSuffix(content, "\n") {
		content += "\n"
	}

	return []byte(content), nil
}

func ensureHookShebang(content string) string {
	if strings.HasPrefix(content, "#!") {
		return content
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Upgrade bd so planner and renderer share the same source-kind set
  2. Re-generate the migration plan with the current binary instead of reusing a stale one
  3. Report a bug if it reproduces on the latest version — this is an internal invariant violation
  4. Add a renderer case for the new SourceKind if you are developing bd and introduced a new kind
Defensive patterns

Strategy: type-guard

Validate before calling

const knownKinds = new Set(['template','hook_file','old','backup']);
if (!knownKinds.has(op.SourceKind)) throw new Error('unknown SourceKind: '+op.SourceKind);

Type guard

func validSourceKind(k hookMigrationSourceKind) bool { switch k { case hookMigrationWriteFromTemplate, hookMigrationWriteFromHookFile, hookMigrationWriteFromOld, hookMigrationWriteFromBackup: return true }; return false }

Try / catch

if err := apply(...); err != nil && strings.Contains(err.Error(), "unknown source kind") {
    // regenerate the plan with the current binary and retry
}

Prevention

When it happens

Trigger: A hookMigrationRetireOp/write op carries a SourceKind outside the four known constants, produced by newer planner code or a corrupted plan structure passed to prepareHookMigrationWrites.

Common situations: Version mismatch where planner and apply code disagree on the enum; hand-built or serialized plans modified externally; a code bug adding a new source kind without a renderer case.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/b20f88ac288e9adc. Report an issue: GitHub.