jashkenas/backbone · error · Error
Backbone.history has already been started
Error message
Backbone.history has already been started
What it means
Backbone.History.start() initializes client-side routing (hashchange/pushState listening), and History.started is a global flag. Starting it twice would register duplicate listeners and re-initialize routing state, so Backbone guards with this explicit error. Only one history instance should ever be started per page load.
Source
Thrown at backbone.js:1880
return path.charAt(0) === '/' ? path.slice(1) : path;
},
// Get the cross-browser normalized URL fragment from the path or hash.
getFragment: function(fragment) {
if (fragment == null) {
if (this._usePushState || !this._wantsHashChange) {
fragment = this.getPath();
} else {
fragment = this.getHash();
}
}
return fragment.replace(routeStripper, '');
},
// Start the hash change handling, returning `true` if the current URL matches
// an existing route, and `false` otherwise.
start: function(options) {
if (History.started) throw new Error('Backbone.history has already been started');
History.started = true;
// Figure out the initial configuration. Do we need an iframe?
// Is pushState desired ... is it available?
this.options = _.extend({root: '/'}, this.options, options);
this.root = this.options.root;
this._trailingSlash = this.options.trailingSlash;
this._wantsHashChange = this.options.hashChange !== false;
this._hasHashChange = 'onhashchange' in window && (document.documentMode === void 0 || document.documentMode > 7);
this._useHashChange = this._wantsHashChange && this._hasHashChange;
this._wantsPushState = !!this.options.pushState;
this._hasPushState = !!(this.history && this.history.pushState);
this._usePushState = this._wantsPushState && this._hasPushState;
this.fragment = this.getFragment();
// Normalize root to always include a leading and trailing slash.
this.root = ('/' + this.root + '/').replace(rootStripper, '/');
View on GitHub (pinned to f229c75b19)
Solutions
- Guard the call: if (!Backbone.History.started) Backbone.history.start();
- Move start() to a single, top-level entry point and remove duplicate invocations.
- Track your own initialized flag around your bootstrap function so it runs only once.
- Ensure the bootstrap script/module isn't loaded or evaluated twice (check script tags and module loading).
Example fix
// before
Backbone.history.start();
// ...later, possibly again:
Backbone.history.start(); // throws
// after
if (!Backbone.History.started) {
Backbone.history.start({pushState: true});
} Defensive patterns
Strategy: validation
Validate before calling
if (!Backbone.History.started) {
Backbone.history.start({pushState: true, root: '/'});
} Try / catch
try {
Backbone.history.start();
} catch (e) {
if (e.message !== 'Backbone.history has already been started') throw e;
// already started: safe to ignore
} Prevention
- Call Backbone.history.start() exactly once, in a single bootstrap module.
- Use the Backbone.History.started flag as a guard before any start() call.
- Check for duplicate script tags or double module evaluation in your bundler config.
- If a wrapper framework may have started routing, rely on its lifecycle instead of calling start() again.
- In tests/hot-reload, fully tear down or reload the page between history starts.
When it happens
Trigger: Calling Backbone.history.start() (or a History subclass instance's start()) more than once in the same page — e.g., calling it both in app bootstrap code and again in a router initialization or an AJAX-complete callback, or re-running a bootstrap script on route change.
Common situations: Duplicated bootstrap code (require/AMD module loaded twice, script included twice); legacy code paths that re-call start() after navigation; frameworks wrapping Backbone that call start() internally while app code also calls it; hot-reload setups re-executing initialization modules.
Related errors
AI-assisted analysis of jashkenas/backbone@f229c75b19 (2026-08-28).
Data as JSON: /api/errors/c3e13a6dc9f41048.
Report an issue: GitHub.