projectdiscovery/katana · error
failed to navigate back to origin page: %s != %s
Error message
failed to navigate back to origin page: %s != %s
What it means
After history-back or shortest-path navigation, isCorrectNavigation compares the current page hash to action.OriginID; when they differ, navigation did not land back on the origin page. This means the page content/hash still mismatches the origin even after the recovery strategies (SimHash tolerance already failed). The caller treats the action as not completing correctly.
Source
Thrown at pkg/engine/headless/crawler/state.go:51
originPageState, err := c.crawlGraph.GetPageState(action.OriginID)
if err != nil {
return "", pageState, fmt.Errorf("failed to get origin page state: %w", err)
}
if pageState != nil && originPageState != nil {
distance := simhash.Distance(pageState.SimHash, originPageState.SimHash)
if distance <= simhashThreshold {
c.logger.Debug("Page is similar enough to origin, proceeding",
slog.String("current_hash", currentPageHash),
slog.String("origin_hash", action.OriginID),
slog.Uint64("simhash_distance", uint64(distance)),
)
// Treat this page as the origin state to avoid creating a new vertex
return originPageState.UniqueID, pageState, nil
}
}
return "", pageState, fmt.Errorf("failed to navigate back to origin page: %s != %s", currentPageHash, action.OriginID)
}
func getPageHash(page *browser.BrowserPage) (string, *types.PageState, error) {
pageState, err := newPageState(page, nil)
if err == ErrEmptyPage {
return emptyPageHash, nil, nil
}
if err != nil {
return "", nil, errors.Wrap(err, "could not get page state")
}
return pageState.UniqueID, pageState, nil
}
var ErrEmptyPage = errors.New("page is empty")
func newPageState(page *browser.BrowserPage, action *types.Action) (*types.PageState, error) {
pageInfo, err := page.Info()
if err != nil {View on GitHub (pinned to e3e742739c)
Solutions
- Broaden the simhashThreshold or normalize volatile page content before hashing so dynamic pages match their origin state.
- Check whether the site redirects or invalidates sessions and re-authenticate/avoid actions on such pages.
- Verify OriginID matches the hashing scheme used by getPageHash/newPageState (same normalization on both sides).
- Log currentPageHash vs action.OriginID at debug level to identify which normalization difference causes the mismatch.
Example fix
// before
return "", pageState, fmt.Errorf("failed to navigate back to origin page: %s != %s", currentPageHash, action.OriginID)
// after
if strings.HasPrefix(currentPageHash, action.OriginID) || isKnownDynamicPage(page) {
c.logger.Debug("accepting approximate origin match", "hash", currentPageHash, "origin", action.OriginID)
return action.OriginID, pageState, nil
}
return "", pageState, fmt.Errorf("failed to navigate back to origin page: %s != %s", currentPageHash, action.OriginID) Defensive patterns
Strategy: fallback
Validate before calling
// before relying on exact match, sanity-check similarity
if simhash.Distance(current.SimHash, origin.SimHash) > simhashThreshold {
log.Printf("warning: origin mismatch, hash=%s origin=%s", currentHash, originID)
} Type guard
func matchesOrigin(currentHash, originID string) bool {
return currentHash == originID
} Try / catch
ok, err := crawler.isCorrectNavigation(action)
if err != nil {
if strings.Contains(err.Error(), "failed to navigate back to origin page") {
// treat action as failed, snapshot page for debugging, continue crawl
}
} Prevention
- Normalize volatile content (timestamps, tokens) before computing page hashes.
- Tune simhashThreshold for dynamic sites instead of relying on exact hash equality.
- Re-authenticate before navigation on sites with session expiry redirects.
When it happens
Trigger: tryBrowserHistoryNavigation or tryShortestPathNavigation executes, getPageHash returns currentPageHash, SimHash distance > threshold, and currentPageHash != action.OriginID — i.e., the browser ended on a different page/state than the origin vertex.
Common situations: Dynamic pages whose hash changes on every render (timestamps, CSRF tokens, A/B variants); history.back() blocked by SPA routing or site redirects; login/session expiry redirecting away from the origin; origin ID computed from a different rendering (headless vs first crawl).
Related errors
- failed to get origin page state: %w
- ErrOutOfScope
- ErrMaxDepthReached
- ErrNoCrawlingAction
- ErrNoNavigationPossible
AI-assisted analysis of projectdiscovery/katana@e3e742739c (2026-09-03).
Data as JSON: /api/errors/1a1c9da826f38a6a.
Report an issue: GitHub.