Repository: helm/helm
File: pkg/engine/engine.go (Go)

Code before:
	errMsg := tokens[len(tokens)-1]
	return fmt.Errorf("parse error at (%s): %s", string(location), errMsg)
}

type TraceableError struct {
	location         string
	message          string
	executedFunction string
}

func (t TraceableError) String() string {
	return t.location + "\n  " + t.executedFunction + "\n    " + t.message + "\n"
}
func cleanupExecError(filename string, err error) error {
	if _, isExecError := err.(template.ExecError); !isExecError {
		return err
	}

	// taken from https://cs.opensource.google/go/go/+/refs/tags/go1.23.6:src/text/template/exec.go;l=138
	// > "template: %s: %s"
	// taken from https://cs.opensource.google/go/go/+/refs/tags/go1.23.6:src/text/template/exec.go;l=141
	// > "template: %s: executing %q at <%s>: %s"

	execErrFmt, compileErr := regexp.Compile(`^template: (?P<templateName>(?U).+): executing (?P<functionName>(?U).+) at (?P<location>(?U).+): (?P<errMsg>(?U).+)(?P<nextErr>( template:.*)?)$`)
	if compileErr != nil {
		return err
	}
	execErrFmtWithoutTemplate, compileErr := regexp.Compile(`^template: (?P<templateName>(?U).+): (?P<errMsg>.*)(?P<nextErr>( template:.*)?)$`)
	if compileErr != nil {
		return err
	}

	tokens := strings.SplitN(err.Error(), ": ", 3)
	if len(tokens) != 3 {
		// This might happen if a non-templating error occurs
		return fmt.Errorf("execution error in (%s): %s", filename, err)
	}

	// The first token is "template"
	// The second token is either "filename:lineno" or "filename:lineNo:columnNo"
	location := tokens[1]

	parts := warnRegex.FindStringSubmatch(tokens[2])
	if len(parts) >= 2 {
		return fmt.Errorf("execution error at (%s): %s", string(location), parts[1])
	}
	current := err
	fileLocations := []TraceableError{}
	maxIterations := 100
	for i := 0; i < maxIterations && current != nil; i++ {
		if current == nil {

Review comment (suggestion):
```suggestion
		return compileErr
```

(but see comment above about `MustCompile`)

Code after:
		}

		// Work around the issue where Go will emit "<no value>" even if Options(missing=zero)
		// is set. Since missing=error will never get here, we do not need to handle
		// the Strict case.
		rendered[filename] = strings.ReplaceAll(buf.String(), "<no value>", "")
	}

	return rendered, nil
}

func cleanupParseError(filename string, err error) error {
	tokens := strings.Split(err.Error(), ": ")
	if len(tokens) == 1 {
		// This might happen if a non-templating error occurs
		return fmt.Errorf("parse error in (%s): %s", filename, err)
	}
	// The first token is "template"
	// The second token is either "filename:lineno" or "filename:lineNo:columnNo"
	location := tokens[1]
	// The remaining tokens make up a stacktrace-like chain, ending with the relevant error
	errMsg := tokens[len(tokens)-1]
	return fmt.Errorf("parse error at (%s): %s", string(location), errMsg)
}

type TraceableError struct {
	location         string
	message          string
	executedFunction string
}

func (t TraceableError) String() string {
	var errorString strings.Builder
	if t.location != "" {
		fmt.Fprintf(&errorString, "%s\n  ", t.location)
	}
	if t.executedFunction != "" {
		fmt.Fprintf(&errorString, "%s\n    ", t.executedFunction)
	}
	if t.message != "" {
		fmt.Fprintf(&errorString, "%s\n", t.message)
	}
	return errorString.String()
}

// reformatExecErrorMsg takes an error message for template rendering and formats it into a formatted
// multi-line error string
func reformatExecErrorMsg(filename string, err error) error {
	// This function matches the error message against regex's for the text/template package.
	// If the regex's can parse out details from that error message such as the line number, template it failed on,
	// and error description, then it will construct a new error that displays these details in a structured way.