projectdiscovery/nuclei · error

could not read profile file: %w

Error message

could not read profile file: %w

What it means

processInlineSecretsFromProfile (cmd/nuclei/main.go:880) reads the profile file with os.ReadFile before extracting its `secrets:` section; any read failure is wrapped as 'could not read profile file' with the os error (%w) carrying the exact cause (ENOENT, EACCES, is-a-directory, etc.). It fires at startup when a profile is supplied, before any scanning starts.

Source

Thrown at cmd/nuclei/main.go:880

	})
	if err != nil && err.Error() != "FOUND" {
		options.Logger.Error().Msgf("%s\n", err)
	}
	return profilePath
}

// profileSecrets is a helper struct to extract secrets section from a template profile YAML
type profileSecrets struct {
	Secrets interface{} `yaml:"secrets"`
}

// processInlineSecretsFromProfile parses the profile YAML file for inline secrets
// and creates a temporary secrets file compatible with nuclei's auth provider.
// Returns the path to the temp file or empty string if no secrets found.
func processInlineSecretsFromProfile(profilePath string, options *types.Options) (string, error) {
	data, err := os.ReadFile(profilePath)
	if err != nil {
		return "", fmt.Errorf("could not read profile file: %w", err)
	}

	var profile profileSecrets
	if err := yaml.Unmarshal(data, &profile); err != nil {
		return "", fmt.Errorf("could not parse profile YAML: %w", err)
	}

	if profile.Secrets == nil {
		return "", nil
	}

	secretsData, err := yaml.Marshal(profile.Secrets)
	if err != nil {
		return "", fmt.Errorf("could not marshal inline secrets: %w", err)
	}

	tempDir := filepath.Join(os.TempDir(), "nuclei-secrets")
	if err := os.MkdirAll(tempDir, 0700); err != nil {

View on GitHub (pinned to 265b3a3dec)

Solutions

  1. Confirm the path exists and is a regular file (`ls -l <path>`)
  2. Use an absolute path for the profile file
  3. Fix readability (ownership/chmod) or copy the profile somewhere readable
  4. In containers, verify the volume actually mounts the profile at that path

Example fix

# before
nuclei -profile profiles/scan.yaml

# after
ls -l /home/user/profiles/scan.yaml
nuclei -profile /home/user/profiles/scan.yaml
Defensive patterns

Strategy: validation

Validate before calling

if fi, err := os.Stat(profilePath); err != nil || fi.IsDir() {
    return fmt.Errorf("profile path %q missing or not a file", profilePath)
}
// optional readability probe
if f, err := os.Open(profilePath); err != nil {
    return fmt.Errorf("profile not readable: %w", err)
} else { _ = f.Close() }

Try / catch

data, err := os.ReadFile(profilePath)
if err != nil {
    if errors.Is(err, fs.ErrNotExist) {
        return fmt.Errorf("profile %s does not exist", profilePath)
    }
    if errors.Is(err, fs.ErrPermission) {
        return fmt.Errorf("profile %s not readable by uid %d", profilePath, os.Getuid())
    }
    return err
}

Prevention

When it happens

Trigger: Running nuclei with a `-profile` path that does not exist, is a directory, or is unreadable by the current user; a relative profile path resolved from a different working directory.

Common situations: Typo'd profile path; profile created by root/another user with restrictive permissions; containers/CI where the profile file was not mounted at the expected path.

Related errors


AI-assisted analysis of projectdiscovery/nuclei@265b3a3dec (2026-08-15). Data as JSON: /api/errors/2be38fced2e77d32. Report an issue: GitHub.