Skip to content

fix(loader): avoid panic when dialers are missing#7027

Open
Crucis918 wants to merge 4 commits intoprojectdiscovery:devfrom
Crucis918:fix-6674-handle-missing-dialers
Open

fix(loader): avoid panic when dialers are missing#7027
Crucis918 wants to merge 4 commits intoprojectdiscovery:devfrom
Crucis918:fix-6674-handle-missing-dialers

Conversation

@Crucis918
Copy link

@Crucis918 Crucis918 commented Feb 25, 2026

Fixes #6674.

When dialers are missing for the current executionId, the loader currently panics and crashes the process.

This changes the behavior to:

  • log an error with executionId
  • attempt protocolstate.Init(typesOpts) once to initialize dialers
  • if still missing: return empty slice (no panic)
  • handle wait group init error gracefully

Local check:

  • go test ./pkg/catalog/loader

/claim #6674

Summary by CodeRabbit

  • Bug Fixes
    • Prevented crashes when required runtime connections are missing; the app now logs the issue and exits the operation gracefully instead of terminating.
    • Avoids starting concurrent work if necessary connection resources are absent, improving startup robustness and reducing wasted processing.

@auto-assign auto-assign bot requested a review from Mzack9999 February 25, 2026 00:54
@neo-by-projectdiscovery-dev
Copy link

neo-by-projectdiscovery-dev bot commented Feb 25, 2026

Neo - PR Security Review

No security issues found

Highlights

  • Changed return value from nil to empty slice []*templates.Template{} for consistency
  • Error handling remains graceful - logs errors and returns empty templates instead of panicking
  • ExecutionId in error messages is a random session identifier (xid), not sensitive data
Hardening Notes

Comment @neo help for available commands. · Open in Neo

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 25, 2026

Walkthrough

LoadTemplatesWithTags no longer panics when no dialers exist for an execution ID. It attempts to initialize dialers via protocolstate.Init(typesOpts), logs errors and returns an empty slice on failure, and defers wait-group creation until after dialers are confirmed present. (≤50 words)

Changes

Cohort / File(s) Summary
Loader runtime behavior
pkg/catalog/loader/loader.go
Replaced panic on missing dialers with an initialization attempt (protocolstate.Init(typesOpts)); on init failure or still-nil dialers, log error and return an empty slice. Moved creation of the wait group (wgLoadTemplates) to after dialer existence check and handle wait-group creation failures with logging and early return.
Manifest metadata
manifest_updates
Small manifest adjustments accompanying the loader change (+14/-6 lines).

Sequence Diagram(s)

sequenceDiagram
    participant Loader
    participant ProtocolState
    participant Dialers
    Loader->>ProtocolState: GetDialersWithId(executionId)
    alt dialers found
        ProtocolState-->>Loader: dialers
        Loader->>Loader: create wait group and proceed with concurrent loading
    else no dialers
        ProtocolState-->>Loader: nil
        Loader->>ProtocolState: Init(typesOpts)
        alt init success and dialers now exist
            ProtocolState-->>Loader: dialers
            Loader->>Loader: create wait group and proceed
        else init failed or still nil
            ProtocolState-->>Loader: nil / error
            Loader-->>Loader: log error and return empty slice
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through code with gentle cheer,
No more panics when dialers disappear,
I nudge Init, I log, then softly part,
Concurrency waits till connections start,
A quiet patch from my tiny heart.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR implements the core requirement from #6674 by replacing the panic with graceful error handling. However, the function signature was not changed and errors are logged/returned as empty slices instead of being propagated via return values as suggested. Consider whether the current approach of returning empty slices without error propagation fully satisfies #6674's expectation that callers 'can handle missing dialers' gracefully, or if function signatures should be updated to explicitly return errors.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(loader): avoid panic when dialers are missing' accurately summarizes the main change in the PR, which replaces a panic with graceful error handling when dialers are not initialized.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the panic issue and gracefully handling missing dialers. The wait group initialization move is a supporting optimization for the error path, not an out-of-scope change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
pkg/catalog/loader/loader.go (1)

709-712: Wait group allocated before the dialers guard — minor ordering nit.

wgLoadTemplates is created at lines 709–712 before the executionId/dialers checks at lines 714–722. If dialers are nil the wait group is created and immediately discarded. Moving the guard before the wait group creation avoids the unnecessary allocation on the error path.

♻️ Proposed reorder
+	if typesOpts.ExecutionId == "" {
+		typesOpts.ExecutionId = xid.New().String()
+	}
+
+	if protocolstate.GetDialersWithId(typesOpts.ExecutionId) == nil {
+		store.logger.Error().Msgf("dialers with executionId %s not found", typesOpts.ExecutionId)
+		return nil
+	}
+
 	wgLoadTemplates, errWg := syncutil.New(syncutil.WithSize(concurrency))
 	if errWg != nil {
 		panic("could not create wait group")
 	}
-
-	if typesOpts.ExecutionId == "" {
-		typesOpts.ExecutionId = xid.New().String()
-	}
-
-	dialers := protocolstate.GetDialersWithId(typesOpts.ExecutionId)
-	if dialers == nil {
-		store.logger.Error().Msgf("dialers with executionId %s not found", typesOpts.ExecutionId)
-		return nil
-	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/catalog/loader/loader.go` around lines 709 - 712, The wait group
wgLoadTemplates is allocated via syncutil.New before the guard that checks
executionId and dialers, causing an unnecessary allocation if dialers are nil;
move the syncutil.New(...) call (and its error handling) so it occurs after the
executionId/dialers validation (the guard around executionId and dialers) to
avoid creating wgLoadTemplates when the function will early-return or panic due
to missing dialers; update references to wgLoadTemplates and errWg accordingly
so the variable is only created after the validation succeeds.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/catalog/loader/loader.go`:
- Around line 720-721: The current early return in LoadTemplatesWithTags
(store.logger.Error().Msgf(...); return nil) hides configuration errors by
returning nil instead of an error; change LoadTemplatesWithTags to return
([]Template, error) and return a descriptive error (e.g., fmt.Errorf with
executionId context) instead of nil, then update all callers (LoadTemplates,
Load, lazy auth loading, tech detection paths, etc.) to accept and propagate
that error up the call chain; if you intentionally want to avoid the refactor,
add a clear code comment at the top of LoadTemplatesWithTags documenting the
deliberate deviation and the risk of silent failures so future readers know why
error propagation was not implemented.
- Around line 718-722: The fetch of dialers via
protocolstate.GetDialersWithId(typesOpts.ExecutionId) creates a variable dialers
that is never used; either make this an explicit precondition check or actually
pass the dialers into downstream logic. Fix by replacing the unused variable
with an inline existence check (e.g., if
protocolstate.GetDialersWithId(typesOpts.ExecutionId) == nil {
store.logger.Error()...; return nil }) if you only need validation, OR wire
dialers into the downstream calls that need executor information (e.g., attach
dialers to store.config.ExecutorOptions or pass it into the template
parsing/loader functions that currently receive store.config.ExecutorOptions,
store.preprocessor, and store.tagFilter). Ensure you update references to
dialers (or remove the variable) in the function to eliminate the dead
assignment.

---

Nitpick comments:
In `@pkg/catalog/loader/loader.go`:
- Around line 709-712: The wait group wgLoadTemplates is allocated via
syncutil.New before the guard that checks executionId and dialers, causing an
unnecessary allocation if dialers are nil; move the syncutil.New(...) call (and
its error handling) so it occurs after the executionId/dialers validation (the
guard around executionId and dialers) to avoid creating wgLoadTemplates when the
function will early-return or panic due to missing dialers; update references to
wgLoadTemplates and errWg accordingly so the variable is only created after the
validation succeeds.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d771daa and 1d8658c.

📒 Files selected for processing (1)
  • pkg/catalog/loader/loader.go

Comment on lines 720 to 721
store.logger.Error().Msgf("dialers with executionId %s not found", typesOpts.ExecutionId)
return nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Returning nil is semantically ambiguous and diverges from the issue's requested error-propagation design.

Issue #6674 explicitly requested updating LoadTemplatesWithTags's signature to propagate an error, and updating callers accordingly. The current change returns nil — which is indistinguishable from "zero templates matched" at every call site (e.g., LoadTemplatesLoad(), lazy auth loading, tech detection paths). Callers silently proceed with zero templates and have no way to detect a misconfigured execution ID vs. a legitimate empty result.

The logging provides a human signal but no programmatic signal. Consider honouring the original design request:

♻️ Proposed signature change (sketch)
-func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templates.Template {
+func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) ([]*templates.Template, error) {
 	defer store.saveMetadataIndexOnce()
 	...
-	if dialers == nil {
-		store.logger.Error().Msgf("dialers with executionId %s not found", typesOpts.ExecutionId)
-		return nil
-	}
+	if protocolstate.GetDialersWithId(typesOpts.ExecutionId) == nil {
+		return nil, fmt.Errorf("dialers with executionId %s not found", typesOpts.ExecutionId)
+	}
 	...
-	return loadedTemplates.Slice
+	return loadedTemplates.Slice, nil
 }

LoadTemplates, Load, and other callers would then need to be updated to handle and propagate the error.

If the current "log and return nil" approach is intentional to avoid a larger refactor, the PR description and/or a code comment should document the deviation and the risk of silent failures at call sites.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/catalog/loader/loader.go` around lines 720 - 721, The current early
return in LoadTemplatesWithTags (store.logger.Error().Msgf(...); return nil)
hides configuration errors by returning nil instead of an error; change
LoadTemplatesWithTags to return ([]Template, error) and return a descriptive
error (e.g., fmt.Errorf with executionId context) instead of nil, then update
all callers (LoadTemplates, Load, lazy auth loading, tech detection paths, etc.)
to accept and propagate that error up the call chain; if you intentionally want
to avoid the refactor, add a clear code comment at the top of
LoadTemplatesWithTags documenting the deliberate deviation and the risk of
silent failures so future readers know why error propagation was not
implemented.

@Crucis918
Copy link
Author

Applied CodeRabbit nit: moved wait group initialization after executionId/dialers guard to avoid unnecessary allocation on error path.

Local check: go test ./pkg/catalog/loader

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
pkg/catalog/loader/loader.go (1)

713-717: ⚠️ Potential issue | 🟠 Major

Return-type semantics are still ambiguous on missing dialers.

At Line 716, returning nil still hides a configuration/runtime error as a normal “no templates” result. This path should return an error so callers can distinguish failure from empty matches and react appropriately.

♻️ Suggested direction
-func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templates.Template {
+func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) ([]*templates.Template, error) {
...
-    if dialers == nil {
-        store.logger.Error().Msgf("dialers with executionId %s not found", typesOpts.ExecutionId)
-        return nil
-    }
+    if protocolstate.GetDialersWithId(typesOpts.ExecutionId) == nil {
+        return nil, fmt.Errorf("dialers with executionId %s not found", typesOpts.ExecutionId)
+    }
...
-    return loadedTemplates.Slice
+    return loadedTemplates.Slice, nil
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/catalog/loader/loader.go` around lines 713 - 717, The code currently logs
"dialers with executionId %s not found" and returns nil from the block that
checks protocolstate.GetDialersWithId(typesOpts.ExecutionId); change this to
return a non-nil error so callers can distinguish a failure to find dialers from
a legitimate "no templates" result. Concretely, replace the lone nil return with
a descriptive error (e.g., fmt.Errorf or errors.Wrap) that includes
typesOpts.ExecutionId, keep the log line, and ensure the enclosing function's
return signature and all callers of the enclosing function are updated to handle
the error return; reference symbols: protocolstate.GetDialersWithId,
typesOpts.ExecutionId, and store.logger.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@pkg/catalog/loader/loader.go`:
- Around line 713-717: The code currently logs "dialers with executionId %s not
found" and returns nil from the block that checks
protocolstate.GetDialersWithId(typesOpts.ExecutionId); change this to return a
non-nil error so callers can distinguish a failure to find dialers from a
legitimate "no templates" result. Concretely, replace the lone nil return with a
descriptive error (e.g., fmt.Errorf or errors.Wrap) that includes
typesOpts.ExecutionId, keep the log line, and ensure the enclosing function's
return signature and all callers of the enclosing function are updated to handle
the error return; reference symbols: protocolstate.GetDialersWithId,
typesOpts.ExecutionId, and store.logger.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d8658c and 22893ec.

📒 Files selected for processing (1)
  • pkg/catalog/loader/loader.go

@Crucis918
Copy link
Author

Addressed CodeRabbit note about semantics:

  • If dialers are missing, attempt protocolstate.Init(typesOpts) and re-fetch dialers
  • If init fails or dialers still missing, log error and return an empty slice (not nil)

Local check: go test ./pkg/catalog/loader

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
pkg/catalog/loader/loader.go (1)

715-723: ⚠️ Potential issue | 🟠 Major

Dialer-init failures are still silently collapsed into “no templates”.

At Line 717 and Line 722, returning an empty slice makes “dialer setup failed” indistinguishable from “no templates matched”. This still prevents callers from reacting programmatically to execution-id misconfiguration.

♻️ Suggested direction
-func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templates.Template {
+func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) ([]*templates.Template, error) {
...
-            return []*templates.Template{}
+            return nil, fmt.Errorf("dialers init failed for executionId %s: %w", typesOpts.ExecutionId, err)
...
-            return []*templates.Template{}
+            return nil, fmt.Errorf("dialers with executionId %s not found", typesOpts.ExecutionId)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/catalog/loader/loader.go` around lines 715 - 723, The code currently
hides dialer initialization failures by returning an empty
[]*templates.Template{} when protocolstate.Init(typesOpts) fails or when
protocolstate.GetDialersWithId(typesOpts.ExecutionId) returns nil; instead
change the loader function to return an error (e.g., change signature from func
...() []*templates.Template to func ...() ([]*templates.Template, error)), and
replace the two returns of an empty slice with returns that wrap the underlying
error (include the protocolstate.Init error and a descriptive error when
GetDialersWithId returns nil referencing typesOpts.ExecutionId). Update all
callers to handle the (templates, error) result and propagate or handle errors
appropriately; use store.logger.Error().Err(err).Msgf(...) to keep logging
consistent while returning the error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/catalog/loader/loader.go`:
- Around line 726-728: The code currently panics when syncutil.New(...) fails;
replace the panic in the wgLoadTemplates creation (wgLoadTemplates, errWg :=
syncutil.New(...)) with graceful error handling: log the error using the
package/logger already used in this file (same style as the surrounding error
handling) and return from the function with that error (or a wrapped error) so
the loader fails gracefully instead of panicking; ensure you reference
wgLoadTemplates and errWg, check errWg != nil, call the existing logger to
record the failure, and return the error to the caller.

---

Duplicate comments:
In `@pkg/catalog/loader/loader.go`:
- Around line 715-723: The code currently hides dialer initialization failures
by returning an empty []*templates.Template{} when protocolstate.Init(typesOpts)
fails or when protocolstate.GetDialersWithId(typesOpts.ExecutionId) returns nil;
instead change the loader function to return an error (e.g., change signature
from func ...() []*templates.Template to func ...() ([]*templates.Template,
error)), and replace the two returns of an empty slice with returns that wrap
the underlying error (include the protocolstate.Init error and a descriptive
error when GetDialersWithId returns nil referencing typesOpts.ExecutionId).
Update all callers to handle the (templates, error) result and propagate or
handle errors appropriately; use store.logger.Error().Err(err).Msgf(...) to keep
logging consistent while returning the error.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 22893ec and ef82ba5.

📒 Files selected for processing (1)
  • pkg/catalog/loader/loader.go

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
pkg/catalog/loader/loader.go (1)

713-724: dialers is assigned twice but never consumed — re-introduced dead assignment.

Both assignments on lines 713 and 719 exist solely for nil-checks. The variable is never passed downstream. Inline the checks to avoid the dead assignment, as was suggested in the prior review round.

Proposed fix
-	dialers := protocolstate.GetDialersWithId(typesOpts.ExecutionId)
-	if dialers == nil {
+	if protocolstate.GetDialersWithId(typesOpts.ExecutionId) == nil {
 		if err := protocolstate.Init(typesOpts); err != nil {
 			store.logger.Error().Msgf("dialers init failed for executionId %s: %s", typesOpts.ExecutionId, err)
 			return []*templates.Template{}
 		}
-		dialers = protocolstate.GetDialersWithId(typesOpts.ExecutionId)
-		if dialers == nil {
+		if protocolstate.GetDialersWithId(typesOpts.ExecutionId) == nil {
 			store.logger.Error().Msgf("dialers with executionId %s not found", typesOpts.ExecutionId)
 			return []*templates.Template{}
 		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/catalog/loader/loader.go` around lines 713 - 724, The code currently
assigns dialers twice via protocolstate.GetDialersWithId(typesOpts.ExecutionId)
but never uses the variable, creating a dead assignment; replace those
assignments with inline nil checks: call
protocolstate.GetDialersWithId(typesOpts.ExecutionId) directly in the if
conditions (e.g., if protocolstate.GetDialersWithId(typesOpts.ExecutionId) ==
nil { ... }) and keep the existing protocolstate.Init(typesOpts) and
store.logger.Error() branches unchanged, removing the unused dialers variable
entirely so there are no redundant assignments.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@pkg/catalog/loader/loader.go`:
- Around line 713-724: The code currently assigns dialers twice via
protocolstate.GetDialersWithId(typesOpts.ExecutionId) but never uses the
variable, creating a dead assignment; replace those assignments with inline nil
checks: call protocolstate.GetDialersWithId(typesOpts.ExecutionId) directly in
the if conditions (e.g., if
protocolstate.GetDialersWithId(typesOpts.ExecutionId) == nil { ... }) and keep
the existing protocolstate.Init(typesOpts) and store.logger.Error() branches
unchanged, removing the unused dialers variable entirely so there are no
redundant assignments.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ef82ba5 and 194f3f1.

📒 Files selected for processing (1)
  • pkg/catalog/loader/loader.go

@Crucis918
Copy link
Author

Addressed the remaining panic path too: if wait-group creation fails, we now log the error and return an empty slice instead of panicking.\n\nLocal check:

@Crucis918
Copy link
Author

(Note: the earlier 'go: cannot find main module' output was from the local shell cwd; comment posted successfully.)

@Crucis918
Copy link
Author

Re: error propagation vs empty slice

Agree that returning an error would be more programmatically explicit, but I kept the existing LoadTemplatesWithTags signature to avoid a wider refactor touching many callers / public API surface.

Current behavior is: no panic; we log the failure (with executionId context) and return an empty slice. This mirrors other loader paths that log-and-continue.

If maintainers prefer the signature change, I can follow up with a separate PR that updates the signature + callers accordingly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace panic with error handling in template loader when dialers are missing

1 participant