shadow1ng/fscan · warning

auth function returned nil result

Error message

auth function returned nil result

What it means

Inside the retry loop, workerTestCredentials calls TestSingleCredential and guards against it returning nil. If it does, the worker synthesizes a failed AuthResult with this message so downstream code never dereferences a nil pointer. This signals a contract violation: TestSingleCredential (or the authFn it wraps) must always return a non-nil AuthResult, including on error.

Source

Thrown at plugins/services/credential_tester.go:359

	authFn AuthFunc,
	serviceName string,
	testConfig ConcurrentTestConfig,
) (*ScanResult, ErrorType) {
	for attempt := 0; attempt < testConfig.MaxRetries; attempt++ {
		// 检查是否应该停止
		select {
		case <-ctx.Done():
			return nil, ErrorTypeUnknown
		default:
		}

		// 测试凭据
		result := TestSingleCredential(ctx, cred, authFn)
		if result == nil {
			result = &AuthResult{
				Success:   false,
				ErrorType: ErrorTypeUnknown,
				Error:     fmt.Errorf("auth function returned nil result"),
			}
		}

		if result.Success {
			if result.Conn != nil {
				_ = result.Conn.Close()
			}
			return &ScanResult{
				Type:     plugins.ResultTypeCredential,
				Success:  true,
				Service:  serviceName,
				Username: cred.Username,
				Password: cred.Password,
			}, ErrorTypeUnknown
		}

		// 根据错误类型决定是否重试
		switch result.ErrorType {

View on GitHub (pinned to 95cc12e753)

Solutions

  1. Fix the authFn so every code path returns a non-nil *AuthResult (return an AuthResult with Success:false and Error set instead of nil).
  2. Audit TestSingleCredential for early-return branches that omit a return value.
  3. Log when this guard fires to identify which credential/attempt produced the nil and add a regression test for that path.
  4. If you own neither function, keep the guard but count these occurrences as internal bugs rather than auth failures.

Example fix

// before
func authFn(ctx context.Context, cred Credential) *AuthResult {
    if err != nil {
        return nil
    }
// after
func authFn(ctx context.Context, cred Credential) *AuthResult {
    if err != nil {
        return &AuthResult{Success: false, ErrorType: ErrorTypeUnknown, Error: err}
    }
Defensive patterns

Strategy: type-guard

Validate before calling

if authFn == nil {
    return errors.New("authFn must not be nil")
}

Type guard

func nonNilResult(r *AuthResult, err error) *AuthResult {
    if r != nil {
        return r
    }
    return &AuthResult{Success: false, ErrorType: ErrorTypeUnknown, Error: fmt.Errorf("nil result: %v", err)}
}

Try / catch

result := TestSingleCredential(ctx, cred, authFn)
if result == nil {
    log.Printf("BUG: TestSingleCredential returned nil for %+v", cred)
    result = &AuthResult{Success: false, ErrorType: ErrorTypeUnknown, Error: errors.New("nil result")}
}

Prevention

When it happens

Trigger: TestSingleCredential returning nil because the wrapped authFn returned nil, or a code path in TestSingleCredential that forgot its return value on an early error branch.

Common situations: Custom authFn implementations that `return nil` on error instead of returning an AuthResult with an Error field; library upgrades where the authFn signature/contract changed; race conditions where a result channel path skips assignment.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of shadow1ng/fscan@95cc12e753 (2026-09-06). Data as JSON: /api/errors/ce8c81305593162f. Report an issue: GitHub.