Documentation menu

Reference

Sharp edges

Last updated October 22, 2026

The behaviors that surprise people, collected in one place. Every one of these is also called out in context on its own page.

  1. authors = GitHub usernames = scope co-ownership. Append-only, and usernames are not verified to exist — a typo silently grants a nonexistent account. (Publishing)
  2. Versions are immutable. There is no unpublish from the CLI — delisting is registry moderation, and existing lockfiles keep working. (Publishing)
  3. embr install wipes and rebuilds the packages-out folders on every full install. Don't keep hand-written files in them — the rebuild deletes them, and the fast path cannot see them. (embr install)
  4. Bare dependencies go to Ember's registry unless [indices] default says otherwise. (embr index)
  5. Workspace dependency version does nothing locally — it only shapes the published requirement. (Workspaces)
  6. cargo install users still need embr self install for tool shims. (Installing Ember)
  7. The packed ember.toml is regenerated on publish — comments dropped, workspace dependencies rewritten. (embr publish)
  8. A no-op install is ~10 ms — except roughly once per 5 minutes, when it spends ~0.7 s re-checking the indices. That is the price of ^ requirements staying live. (embr install)
  9. New releases of ^ dependencies appear at most 5 minutes late unless you pass --refresh. (embr install)
  10. The first install after upgrading to embr 0.3.0 re-clones each index once — the cache key scheme changed, and embr cache clean removes the orphaned old clones. (embr cache)
  11. Archives are cached on the assumption that published versions are immutable. If a registry ever mutates one, --refresh or embr cache clean is the way out. (embr cache)
  12. Override path segments are aliases as each package spelled them, and they are case-sensitive — for wally packages that usually means "Comm.Signal", not "comm.signal". Ember warns when a path matches nothing. (Overrides)
  13. An override redirects one edge, not the whole install. If another dependency still asks for the original package, both the original and the replacement end up installed, on purpose. (Overrides)
  14. embx name@version against a pinned alias is an error, not an override. Drop the version to run the pin, or use the full owner/repo form to run something else. (embx)
  15. embr cache clean leaves the tool store alone. Tool and embx downloads are cleared by embr tool cache clean instead. (embr cache)
  16. embx executes code fetched from GitHub by name — the same trust decision as installing a dependency. Prefer @version pins in scripts and CI. (embx)
  17. Only declared dependencies get a top-level link. A transitive package is installed, but reachable only by the package that asked for it; require it yourself by adding it to [dependencies]. (Dependencies & installing)
  18. Environment roots are self-contained, so a package needed by both a shared and a server dependency installs twice — possibly at different versions, because conflicts are judged per root. (Dependencies & installing)
  19. ember.lock version 2 is breaking. Older embr can't read it, and --locked refuses a v1 lock; run one plain embr install to migrate. (Dependencies & installing)
  20. A patch that cannot apply is a hard error, never a silent skip. A [patches] key whose package left the graph, resolved to a different version, or covers only some environment roots stops the install — an unapplied patch could be someone's security fix. (embr patch)
  21. Patch keys pin an exact version, so a ^ dependency picking up a new release fails the install until the patch is re-done against it or removed — your fix is never silently dropped. (embr patch)
  22. Only six scripts can drop the run. build, test, start, serve, dev and fmt/format are subcommands, and so is a name that starts with one of them and a colon, such as build:prod. embr lint is not a command even when lint is in [scripts], and no script can shadow a real Ember command. (embr run)
  23. prepublish runs on --dry-run too, because a dry run exists to show the archive a real publish would produce — and the hook is what builds it. (Lifecycle hooks)
  24. In a workspace, each manifest's hooks bracket only its own portion and run in its own directory, so a root postinstall fires once, after every member is installed. (Lifecycle hooks)
  25. A list script gives each command a pty on macOS/Linux, so stdout and stderr arrive merged — a terminal has one stream. Redirecting Ember's output switches the whole thing back to pipes, which separates them again and drops colour. (embr run)
  26. A failing command in a list does not stop the others. They all run to their own end, and Ember exits with the first non-zero code once they have finished. (embr run)
  27. A pre/post entry is only a hook if something answers to the rest of the name. prelude is an ordinary script, and so is a prebuild in a manifest with no build. (Lifecycle hooks)
  28. Tool assets compressed with zstd, bzip2, 7z or rar are an error, not an install. Ember names the format instead of writing the archive to the binary's path. (embr tool)