sipeed/picoclaw · error

credential: file:// reference has no filename

Error message

credential: file:// reference has no filename

What it means

Returned by Resolver.Resolve when a credential value starts with `file://` but the remainder is empty or only whitespace. The file scheme promises an indirection ("read the secret from this file in the config dir"), so a bare scheme with no filename is a malformed reference, not a lookup miss. Empty-string credentials short-circuit earlier, so this only fires for literal `file://` (or `file:// `).

Source

Thrown at pkg/credential/credential.go:119

		}
	}
	return &Resolver{configDir: configDir, resolvedConfigDir: resolved}
}

// Resolve returns the actual credential value for raw:
//
//   - ""                → "" (no error; auth_method=oauth needs no key)
//   - "file://name.key" → trimmed content of configDir/name.key
//   - anything else     → raw unchanged (plaintext credential)
func (r *Resolver) Resolve(raw string) (string, error) {
	if raw == "" {
		return "", nil
	}

	if strings.HasPrefix(raw, FileScheme) {
		fileName := strings.TrimSpace(strings.TrimPrefix(raw, FileScheme))
		if fileName == "" {
			return "", fmt.Errorf("credential: file:// reference has no filename")
		}

		baseDir := r.resolvedConfigDir
		if baseDir == "" {
			baseDir = r.configDir
		}
		keyPath := filepath.Join(baseDir, fileName)
		// Resolve symlinks before enforcing containment to prevent escaping via symlinks.
		realKeyPath, err := filepath.EvalSymlinks(keyPath)
		if err != nil {
			return "", fmt.Errorf("credential: failed to resolve credential file path %q: %w", keyPath, err)
		}
		if !isWithinDir(realKeyPath, baseDir) {
			return "", fmt.Errorf("credential: file:// path escapes config directory")
		}
		data, err := os.ReadFile(realKeyPath)
		if err != nil {
			return "", fmt.Errorf("credential: failed to read credential file %q: %w", realKeyPath, err)

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Complete the reference: `file://name.key` where name.key lives in the config dir
  2. If you meant an empty credential (e.g. oauth), use an empty string instead of file://
  3. Check templating/envsubst output for unfilled ${...} placeholders that leave file:// bare

Example fix

# before
api_key: file://

# after
api_key: file://openai.key
Defensive patterns

Strategy: validation

Validate before calling

// Validate file:// references before resolving.
func fileRefHasName(raw string) error {
	if strings.HasPrefix(raw, "file://") {
		if strings.TrimSpace(strings.TrimPrefix(raw, "file://")) == "" {
			return fmt.Errorf("file:// reference %q has no filename", raw)
		}
	}
	return nil
}

Type guard

func isCompleteFileRef(raw string) bool {
	if !strings.HasPrefix(raw, "file://") { return true }
	return strings.TrimSpace(strings.TrimPrefix(raw, "file://")) != ""
}

Try / catch

val, err := resolver.Resolve(raw)
if err != nil {
	if strings.Contains(err.Error(), "no filename") {
		// config authoring bug — fail fast with config file/line context
	}
	return "", err
}

Prevention

When it happens

Trigger: Config contains `api_key: file://` or `api_key: "file:// "`. strings.TrimPrefix strips the scheme, TrimSpace removes whitespace, and the empty remainder triggers the error.

Common situations: Template placeholders left unfilled (`api_key: file://${KEY_FILE}`), users writing file:// intending "use some file" without knowing the filename is required, or trailing- edit accidents deleting the filename.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/9b758afad228212f. Report an issue: GitHub.