anomalyco/sst · error

handler not found: (searched candidate paths)

Error message

handler not found: %s (searched %d candidate paths)

What it means

findPythonFile iterates every generated candidate path (project root, plus src/, app/, functions/, lambda/, handlers/, lib/ subdirectories, with and without the .py extension) and throws this when none is a regular .py file. The message reports the handler string and how many candidate paths were searched, aiding diagnosis of where the resolver looked.

Solutions

  1. Confirm the exact file path (without .handler suffix) exists, e.g. ls <projectRoot>/src/index.py.
  2. Fix the handler field to a path relative to the project root, e.g. "src/index.handler".
  3. If the code lives in a non-standard directory, either move it into root/src/ or place a pyproject.toml and align paths.
  4. Check that the deploy root (resolved from cfgPath) is the directory containing your code, not a parent or child.

Example fix

// before
handler: "functions/process.fn"
// (no functions/process.py on disk)

// after
handler: "src/process.fn"  // src/process.py exists
Defensive patterns

Strategy: validation

Validate before calling

// shell
test -f <root>/src/index.py && echo OK || echo "handler file missing"
// generic check: file must exist under root or root/{src,app,functions,lambda,handlers,lib}

Try / catch

try { await deploy() } catch (e) {
  if (String(e).includes("handler not found:"))
    console.error("Searched candidate dirs; confirm file exists:", String(e));
  throw e;
}

Prevention

When it happens

Trigger: resolveHandler was given a handler path like "src/index.handler" but no file matching src/index (or src/index.py) exists under the resolved projectRoot; also fires for empty/incorrect handler values that still yield candidates.

Common situations: Renamed or deleted the handler file after updating code; handler path includes a leading './' or absolute path the candidate generator doesn't expect; monorepo where the Python app lives in a subdirectory not covered by the candidate list; handler uses underscore/hyphen inconsistently with the file name.

Related errors


AI-assisted analysis of anomalyco/sst@a0bd20f762 (2026-08-30). Data as JSON: /api/errors/e8fdd39f88500df4. Report an issue: GitHub.

Appendix: source

Thrown at pkg/runtime/python/project.go:97

	return info, nil
}

// findPythonFile locates the Python file for the given handler path.
func findPythonFile(projectRoot, handlerPath string) (string, error) {
	filePath := extractFilePath(handlerPath)
	candidates := generateCandidatePaths(projectRoot, filePath)

	for _, candidate := range candidates {
		if info, err := os.Stat(candidate); err == nil && info.Mode().IsRegular() && strings.HasSuffix(candidate, ".py") {
			absPath, err := filepath.Abs(candidate)
			if err != nil {
				return "", fmt.Errorf("failed to get absolute path for %s: %w", candidate, err)
			}
			return absPath, nil
		}
	}

	return "", fmt.Errorf("handler not found: %s (searched %d candidate paths)", handlerPath, len(candidates))
}

// extractFilePath extracts the file path from a handler path.
func extractFilePath(handlerPath string) string {
	if lastDot := strings.LastIndex(handlerPath, "."); lastDot != -1 {
		return handlerPath[:lastDot]
	}
	return handlerPath
}

// generateCandidatePaths creates a list of potential file locations.
func generateCandidatePaths(projectRoot, handlerPath string) []string {
	var candidates []string

	// Direct path and with .py extension
	candidates = append(candidates, filepath.Join(projectRoot, handlerPath))
	if !strings.HasSuffix(handlerPath, ".py") {
		candidates = append(candidates, filepath.Join(projectRoot, handlerPath+".py"))

View on GitHub (pinned to a0bd20f762)