errorpage

package
v0.33.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 6 Imported by: 0

Documentation

Overview

Package errorpage renders the development error page.

The data comes from the Collector, which is part of the core, so the page knows the queries, the dumps, the events and the routes without any extra package installed.

Absolute rule: nothing in this package may be reachable when Env is not dev. The Recover middleware is the only caller, and it checks the flag first.

This package is a bridge. It is removed in v1.0.0; import github.com/arandu-io/hesape/exception directly.

The page moved to github.com/arandu-io/hesape/exception, where it is one part of the exception handler rather than a package of its own: the same Handler that decides a failure is a 404 draws the page for the one that is a defect. Two things came out of that, and both are why half of this file is envelope rather than alias:

  • Render and RenderDump are no longer exported. They are methods on the Handler, and the Handler is reached through Recover, the Displayer or Render(err). Both functions below therefore build a Handler and drive it.
  • Options is not Config. The hesape struct carries five more fields -- Dev, Views, DontReport, RenderJSONWhen, Console -- and this one may not grow them without changing what the thirteen repositories construct, so Options stays declared here and is translated.

StackFrame and Capture came across whole. Capture decides "framework or application" by asking about hesape's own import path and about the standard library, and collapses everything that is neither only when Options.AppModule says which module the application is. See Capture for what that leaves.

The death date above is what keeps this from being a second way to import one type.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func EditorLink(editor, file string, line int) string

EditorLink builds the link that opens the file straight in the IDE.

It forwards to observability.EditorLink, which is where the one implementation lives: the console needs the same links, and two copies is two places to add the next editor. That package is itself a bridge, so the table answering is hesape/log's -- see its note on what an unset editor now gets.

func Render

func Render(w http.ResponseWriter, r *http.Request, panicValue any, col *observability.Collector, opts Options)

Render draws the full error page.

It is hesape/exception's debug displayer, which is the exported door to the page. Three things about that are worth knowing before reading a page drawn through here:

  • The status is the failure's answer rather than a fixed 500. A panic nothing classifies is still 500; one carrying a status -- an Abort, or one of the sentinels hesape knows -- is now drawn with that status instead of being reported to the client as an internal error.
  • The stack has three more collapsed frames on top: this function, the deferred recover that called it, and runtime.gopanic. hesape captures inside the displayer, which is one hop further out than the old capture from inside this function. The line that panicked is still on the stack and still the first expanded frame.
  • A panic value that is not an error is wrapped, so the subtitle -- the Go type of what failed -- reads as the wrapper rather than as the original. The headline and the message are unchanged, because both are built from the text. hesape has no exported entry that takes the raw value.

func RenderDump

func RenderDump(w http.ResponseWriter, r *http.Request, col *observability.Collector, opts Options)

RenderDump draws the dump page, for the DumpDie flow.

The dump page is drawn by an unexported method whose one caller is Recover, on the branch that recognises the dump-and-die sentinel. So this is Recover, handed a handler that raises that sentinel: hesape catches it, sees development, and draws the page. It is indirect and it is the whole of the exported surface that reaches this page -- see the note in the report.

Two details keep it honest. The sentinel is unexported in hesape/log and DumpDie is the only door to it, so DumpDie is what raises it; the context passed carries no Collector, so its recording half does nothing and no phantom entry is added to the page the caller is about to see. And Recover answers the dump page before it logs anything, so nothing is written to the log that the old path did not write.

Types

type Options

type Options struct {
	// Editor is the target of the "open in IDE" links: vscode, cursor, goland
	// or zed.
	Editor string
	// AppModule is the module path of the application, used to tell app frames
	// from framework and stdlib frames.
	AppModule string
	// Diagnose collects what the registered modules have to say about the state
	// of the system right now. Pass kernel.Diagnose.
	//
	// It exists because the most useful hint is often about something that
	// happened outside this request: the outbox has been stuck for four minutes,
	// the scheduler last ran an hour ago. A page that only looks at the request
	// cannot see any of it, and that is exactly the state where somebody is
	// staring at an error wondering what changed.
	Diagnose func(ctx context.Context) []string
}

Options carries what the page needs from the application configuration.

It stays declared here rather than aliasing hesape/exception.Config, which carries five fields more: Dev, Views, DontReport, RenderJSONWhen and Console. Aliasing would put all five on a struct the skeletons construct as a literal and would move the meaning of Dev into a package whose absolute rule is that it is unreachable outside development. The translation is below, and it is three fields wide.

type StackFrame

type StackFrame = exception.StackFrame

StackFrame is one frame of the stack, already enriched with the source snippet around the failing line.

func Capture

func Capture(skip int, appModule string) []StackFrame

Capture collects the stack from skip onwards, marking which frames belong to the application: application frames are expanded by default and everything else is collapsed.

appModule is the caller's module path.

It is hesape/exception.Capture, and that is the point of it rather than an implementation detail. Collapsing by a hard-coded github.com/arandu-io/framework would match no hesape frame, so every one of them would be called application code, expanded, and its source read off disk on every page. The rule hesape applies is its own import path, the standard library, and package main -- and, for everything else, the module path the application declared.

What that leaves: with appModule empty there is nothing left to tell the application from what it imported, and hesape is generous on purpose -- a page that collapses the frame somebody is looking for is worse than one that expands a frame they are not. So an empty AppModule expands the frames of this bridge along with everybody else's. Both skeletons set it, and it is the field Options exists for.

The snippet is five lines each side of the failing one: the page is laid out for eleven lines.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL