{"record":{"id":"1a1c9da826f38a6a","repo":"projectdiscovery/katana","slug":"failed-to-navigate-back-to-origin-page-s-s","errorCode":null,"errorMessage":"failed to navigate back to origin page: %s != %s","messagePattern":"failed to navigate back to origin page: (.+?) != (.+?)","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/engine/headless/crawler/state.go","lineNumber":51,"sourceCode":"\toriginPageState, err := c.crawlGraph.GetPageState(action.OriginID)\n\tif err != nil {\n\t\treturn \"\", pageState, fmt.Errorf(\"failed to get origin page state: %w\", err)\n\t}\n\n\tif pageState != nil && originPageState != nil {\n\t\tdistance := simhash.Distance(pageState.SimHash, originPageState.SimHash)\n\t\tif distance <= simhashThreshold {\n\t\t\tc.logger.Debug(\"Page is similar enough to origin, proceeding\",\n\t\t\t\tslog.String(\"current_hash\", currentPageHash),\n\t\t\t\tslog.String(\"origin_hash\", action.OriginID),\n\t\t\t\tslog.Uint64(\"simhash_distance\", uint64(distance)),\n\t\t\t)\n\t\t\t// Treat this page as the origin state to avoid creating a new vertex\n\t\t\treturn originPageState.UniqueID, pageState, nil\n\t\t}\n\t}\n\n\treturn \"\", pageState, fmt.Errorf(\"failed to navigate back to origin page: %s != %s\", currentPageHash, action.OriginID)\n}\n\nfunc getPageHash(page *browser.BrowserPage) (string, *types.PageState, error) {\n\tpageState, err := newPageState(page, nil)\n\tif err == ErrEmptyPage {\n\t\treturn emptyPageHash, nil, nil\n\t}\n\tif err != nil {\n\t\treturn \"\", nil, errors.Wrap(err, \"could not get page state\")\n\t}\n\treturn pageState.UniqueID, pageState, nil\n}\n\nvar ErrEmptyPage = errors.New(\"page is empty\")\n\nfunc newPageState(page *browser.BrowserPage, action *types.Action) (*types.PageState, error) {\n\tpageInfo, err := page.Info()\n\tif err != nil {","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/projectdiscovery/katana/blob/e3e742739c3746f085943ce918fb4e2b8daf6fe6/pkg/engine/headless/crawler/state.go#L33-L69","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before\nreturn \"\", pageState, fmt.Errorf(\"failed to navigate back to origin page: %s != %s\", currentPageHash, action.OriginID)\n// after\nif strings.HasPrefix(currentPageHash, action.OriginID) || isKnownDynamicPage(page) {\n    c.logger.Debug(\"accepting approximate origin match\", \"hash\", currentPageHash, \"origin\", action.OriginID)\n    return action.OriginID, pageState, nil\n}\nreturn \"\", pageState, fmt.Errorf(\"failed to navigate back to origin page: %s != %s\", currentPageHash, action.OriginID)","handlingStrategy":"fallback","validationCode":"// before relying on exact match, sanity-check similarity\nif simhash.Distance(current.SimHash, origin.SimHash) > simhashThreshold {\n    log.Printf(\"warning: origin mismatch, hash=%s origin=%s\", currentHash, originID)\n}","typeGuard":"func matchesOrigin(currentHash, originID string) bool {\n    return currentHash == originID\n}","tryCatchPattern":"ok, err := crawler.isCorrectNavigation(action)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to navigate back to origin page\") {\n        // treat action as failed, snapshot page for debugging, continue crawl\n    }\n}","preventionTips":["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."],"tags":["crawler","navigation","simhash"],"backgroundTag":"navigation-target-mismatch","analyzedSha":"e3e742739c3746f085943ce918fb4e2b8daf6fe6","analyzedAt":"2026-09-03T14:55:13.248Z","contentChangedAt":"2026-09-03T14:55:13.248Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}