kubernetes/kops · error

unable to read template: %s, error: %s

Error message

unable to read template: %s, error: %s

What it means

Returned by RunToolBoxTemplate when os.ReadFile fails on a template file that expandFiles already listed. The glob expansion succeeded but the individual template file could not be read — usually a permission problem, the file disappearing between listing and read, or a dangling symlink. The template path and underlying OS error are included in the message.

Source

Thrown at cmd/kops/toolbox_template.go:179

			if err != nil {
				return fmt.Errorf("unable to read snippet: %s, error: %s", j, err)
			}
			snippets[path.Base(j)] = string(content)
		}
	}

	channel, err := kopsapi.LoadChannel(f.VFSContext(), options.channel)
	if err != nil {
		return fmt.Errorf("error loading channel %q: %v", options.channel, err)
	}

	// @step: render each of the templates, splitting on the documents
	r := templater.NewTemplater(channel)
	var documents []string
	for _, x := range templates {
		content, err := os.ReadFile(x)
		if err != nil {
			return fmt.Errorf("unable to read template: %s, error: %s", x, err)
		}

		rendered, err := r.Render(string(content), context, snippets, options.failOnMissing)
		if err != nil {
			return fmt.Errorf("unable to render template: %s, error: %s", x, err)
		}
		// @check if the content is zero ignore it
		if len(rendered) <= 0 {
			continue
		}

		if !options.formatYAML {
			documents = append(documents, strings.Split(rendered, "---\n")...)
			continue
		}

		for _, x := range strings.Split(rendered, "---\n") {
			var data map[string]interface{}

View on GitHub (pinned to 4c8573c808)

Solutions

  1. Check permissions on the file named in the error (ls -l) and grant read access
  2. Confirm the file exists and isn't a broken symlink: ls -lL <file>
  3. Ensure no concurrent job deletes/moves files in the templates directory during the run
  4. Run the command as a user with read access to all template files

Example fix

// before
-rw------- ./templates/cluster.yaml (owned by ci-user, run as deploy-user)

// after
chmod a+r ./templates/cluster.yaml
Defensive patterns

Strategy: try-catch

Validate before calling

matches, _ := filepath.Glob(templateDir)
for _, f := range matches {
    fi, err := os.Stat(f)
    if err != nil || fi.IsDir() || fi.Mode().Perm()&0o400 == 0 {
        return fmt.Errorf("template %s unreadable", f)
    }
}

Try / catch

content, err := os.ReadFile(templateFile)
if err != nil {
    switch {
    case errors.Is(err, os.ErrPermission):
        return fmt.Errorf("grant read access to %s", templateFile)
    case errors.Is(err, os.ErrNotExist):
        return fmt.Errorf("template %s removed since glob expansion", templateFile)
    default:
        return err
    }
}

Prevention

When it happens

Trigger: A template file matched by --template-path glob is unreadable: permissions deny read, file deleted between expandFiles and ReadFile (concurrent build cleanup), broken symlink, or the path points at a special/unreadable file.

Common situations: Templates with 0600 modes owned by another user; running as a different user in CI than the file owner; tmpdirs or generated template directories being wiped mid-run; symlinked template repos pointing at missing checkouts.

Related errors


AI-assisted analysis of kubernetes/kops@4c8573c808 (2026-09-05). Data as JSON: /api/errors/356bbbd7990dc480. Report an issue: GitHub.