Andrew Fribush

← All posts

Three algebra errors in "Existential Risk and Growth"

· November 12, 2026 · 12 min read

Printed page 51 of Leopold Aschenbrenner's September 2, 2019 working draft of "Existential Risk and Growth" has four lines of algebra and then a paragraph. The second line is rho − v̇/v + delta = u(c)/v. The third line, equation (103), solves that for v and prints rho − delta + g_v in the denominator. Between those two lines the sign of delta flips. I tried it with flow utility 1, discount rate .02, hazard .01 and v constant, so g_v is 0. From the second line, v = 1/.03, which is 33.3. From (103), 1/.01, which is 100. I put 100 into the second line and got rho = 0, where I had set .02.

The draft is version 0.5, the one linked in Tyler Cowen's endorsement of September 11, 2019. It is not the later paper coauthored with Philip Trammell. I audited the 2019 draft on 2026-09-13 by back-substitution in exact rational arithmetic, reading the three pages in question as rendered images. It has three algebra errors in its displayed equations: a reversed sign in the value of survival that reaches Proposition 6, a factor missing from the transition system on page 74, and a wrong sign in the tail integral behind the survival probability on page 80. The paper's main growth-rate identities pass 81 exact checks. The audit rejects a blanket description of the derivations as sound. It doesn't claim the paper's qualitative conclusions fall, and it can't tell from the manuscript whether the reported figures were computed from the erroneous equations.

The scope is the draft as printed: the main text, Appendices A.1 to A.11 and B.1 to B.2. I checked displayed equations by substituting each result into the line that produced it, with Python's Fraction type so there's no rounding to hide a miss, and I read the three error locations off rendered page images rather than extracted text. I didn't replicate the numerical transition figures or see the code behind them.

The Sign Flips Between Two Consecutive Lines on Page 51

The model is a growth model with an instantaneous hazard rate delta of existential catastrophe, measured per year, and the planner splits labor among consumption, safety and research. Consumption raises delta and safety lowers it. (In the Hamiltonian as checks.py writes it, the hazard is proportional to c to the epsilon times h to the minus beta, with c consumption and h safety output.) The variable v is the shadow value of survival, what staying alive is worth in utility units. Equation (29), repeated as (91), is its costate condition: rho = g_v + u(c)/v − delta, where rho is the discount rate and g_v is the growth rate of v. Solved for v, the denominator is rho + delta − g_v. With u and g_v held fixed, a higher hazard makes survival worth less, so the plus sign on delta in the denominator is the right one.

The paper's (32) and (33), and again (103) and (104), print the denominator as rho − delta + g_v. Both signs are flipped. On page 51 the line directly above (103) still has plus delta on the left, so the flip happens in the one step that moves everything but the utility term to the other side. Text extracted from a LaTeX PDF drops fraction bars and moves subscripts around, so I rendered the page and read the two lines in the image before calling it an error. That image is sign.png in the audit folder.

fig. 2.14.1 · the value of survival as the hazard rises, equation (29) against the printed (32), (33), (103) and (104).
PRINTED v = 100 FAILS (29); 33.3 SATISFIES ITONE LINE IS ONE FORMULA FOR v AS THE HAZARD RISES (u = 1, rho = 0.02, g_v = 0);ACCENT IS THE DRAFT’S, DOTS THE AUDIT’S CHECK.05010000.010.02 = rho0.030.04HAZARD delta, PER YEARDRAG THE SHEET FOR YOUR OWN HAZARDvINFINITE AT rho,NEGATIVE PAST ITCORRECT, FROM (29): v = u / (rho + delta − g_v)PRINTED (32), (103):v = u / (rho − delta + g_v)100 PRINTED · OFF (29) BY -0.02033.3 CORRECT · (29) HOLDS

Equation (29) of the 2019 draft, rho = g_v + u(c)/v − delta, gives v = u(c)/(rho + delta − g_v). The next lines, (32) and (33), repeated as (103) and (104) on printed page 51, divide by rho − delta + g_v instead. Checked with exact rationals in checks.py.

source: Existential Risk and Growth derivation audit, checks.py line 7 and results.json (eq103_constant_flow); audit.md lines 9-23 · costateSign.data.ts

The figure draws both formulas with u = 1, rho = .02 and g_v = 0 as the hazard runs from 0 to .04. At delta = 0 the two agree at 50, because the delta term is the only one that differs. The correct line falls to 16.7 at delta = .04. The printed line climbs, goes infinite at delta = rho, and is negative past it, which gives survival a value below zero. The audit's check is the pair of dots at delta = .01: 100 printed, 33.3 correct. The cursor reads the printed v's residual in (29) at the hazard under it. Away from the pole at delta = .02 it is −2 delta, because the printed v makes u/v equal rho − delta where (29) needs rho + delta.

The sign carries into Proposition 6. For gamma under 1 the paper's own asymptotic structure is that consumption grows at a rate g_c above 0, delta is constant, delta times v-tilde tends to 1/epsilon, and g_v = (1 − gamma) g_c. With that structure kept, the corrected costate equation gives a hazard of [(1 − gamma) rho − (1 − gamma)² g_c] divided by [epsilon − (1 − gamma)], where that denominator is nonzero, and the audit records that the candidate meets the proposition's other admissibility conditions. The paper's (62), repeated as (147), has a plus sign in both places. I took parameters that satisfy the proposition's own inequalities: epsilon .4, beta .3, gamma .8, rho .02, n .01, and the composite alpha lambda/(1 − phi) set to 1. The proposition's growth formula gives g_c = .005. The printed hazard is .007 and the corrected candidate is .019. With .007 in the costate condition the residual is −.012. With .019 it's zero. (At these parameters the residual is the hazard minus .019.)

The headline numerical example runs on the gamma above 1 branch. On that branch g_v and delta both go to zero, so the sign correction doesn't by itself refute that branch's leading growth rates. The gamma under 1 proposition is the one it reaches: it is stated for general parameters and fails its own necessary condition at parameters that meet its stated inequalities.

Equation (174) Drops a Factor the Line Above It Still Carries

Page 74 is in Appendix A.10, where the draft derives the laws of motion of its transition system. Two of its equations are coupled. Equation (167) gives S = theta_sigma (B + omega_sigma L) and (173) gives L = theta_ell (A + omega_ell S), where L and S are the paper's ell-hat and sigma-hat, the growth rates of the labor shares ell and sigma, and A and B are expressions the appendix defines from the state at the time, in (166) and (172). Substituting (167) into (173) puts an L term on the right that carries the product of all four coefficients, so moving it across gives a denominator of 1 − theta_ell omega_ell theta_sigma omega_sigma. The page does the substitution in three lines. The first line has theta_ell outside the bracket, multiplying everything inside it, including the term in L. The second line moves that term across and writes the bracket as 1 − omega_ell omega_sigma theta_sigma, without the theta_ell. Equation (174) keeps it out, and so does (65), in the main text, where the same expression appears.

fig. 2.14.2 · the printed L of equation (174) sent once round (167) and (173), beside the corrected L.
PRINTED L 0.0486 COMES BACK 0.0535; 0.0532 RETURNSRIGHT IS THE L PUT INTO (167) THEN (173), UP IS THE L BACK; ON THE DIAGONAL ANL RETURNS ITSELF. ACCENT IS THE DRAFT’S.0.0400.0400.0450.0450.0500.0500.0550.0550.0600.060L PUT INL OUT(167) S = theta_sigma (B + omega_sigma L)(173) L = theta_ell (A + omega_ell S)COMES BACKAS ITSELFONE ROUND TRIPMISSES ITSELF BY 0.0049CORRECTED 0.0532RETURNS ITSELFPRINTED (174)0.0486 IN, 0.0535 OUT

Printed (174), also (65), divides by 1 − omega_ell omega_sigma theta_sigma = 1.1680; the line above it keeps theta_ell outside the bracket, so the denominator is 1 − theta_ell omega_ell omega_sigma theta_sigma = 1.0672. Parameters theta_ell .4, omega_ell .3, theta_sigma .7, omega_sigma −.8, A .1, B .2, as in checks.py.

source: Existential Risk and Growth derivation audit, checks.py line 30 and results.json (eq174); audit.md lines 37-51 · factorRoundTrip.data.ts

The check is a round trip, a value of L sent through (167) to get S and then through (173) to get L back. With theta_ell .4, omega_ell .3, theta_sigma .7, omega_sigma −.8, A .1 and B .2, the round trip is a straight line, .0568 − .0672 L, and the only L that returns as itself is where that line crosses the diagonal. The corrected formula gives .0532234 (numerator .0568, denominator 1.0672) and it returns exactly. The printed (174) gives .0486301, with denominator 1.168. Sent round once it comes back as .0535, which is .0049 from where it started. In exact rationals the miss is −.00490192, which rules out rounding.

The manuscript doesn't say whether the simulation code implements (174) or the correct expression. If it implements (174) as printed, it solves a different transition system from the one the paper sets up.

Equation (194) Integrates a Growing Hazard and Adds a Negative Tail

Page 80, in Appendix B.1, turns the simulated hazard path into a survival probability. The probability of surviving to infinity, given survival to today, is e to the minus the integral of the hazard from today onward. The simulation runs to a year T. After T the page says the economy is near its steady state and the hazard declines exponentially, and it checks that the terminal rate is approximately g_delta, the signed growth rate of the hazard, which is negative on the path in question. So the hazard s years after T is delta_T exp(g_delta s), and its integral from zero to infinity is −delta_T/g_delta, a positive number because g_delta is negative. Equation (194) writes the integrand as delta_T exp(−s g_delta), and the result as delta_T/g_delta. With g_delta negative, that integrand grows without bound and that result is below zero.

fig. 2.14.3 · the decaying terminal hazard and its tail integral beside the integrand and sum printed in equation (194).
TAIL ADDS +0.2045; THE PRINTED (194) GIVES −0.2045ONE LINE IS THE HAZARD s YEARS AFTER T (delta_T = 0.0005, g_delta = −0.00244);SHADED IS ITS TAIL INTEGRAL; ACCENT IS THE DRAFT’S.00.00040.00080.0012TT + 500T + 1,000T + 1,500YEARS AFTER THAZARD PER YEARTAIL AS DEFINED,delta_T e^(g_delta s)DECAYS; TAIL +0.2045PRINTED (194)INTEGRATES e^(−s g_delta),WHICH GROWSTHEN (194) ADDSdelta_T / g_delta= -0.2045

g_delta is the signed growth rate of the hazard on the asymptotic path, negative here, so the terminal hazard delta_T e^(g_delta s) decays and its integral from T to infinity is −delta_T / g_delta. 97.4% of that area lies inside the 1,500 years drawn. Parameters from Appendix B.1 as used in checks.py.

source: Existential Risk and Growth derivation audit, checks.py lines 58-62 and results.json (eq194); audit.md lines 55-67 · tailIntegral.data.ts

With the Appendix B.1 parameters, delta_T is .0005 and g_delta is −.00244444 (that is −(gamma − 1) g_c with gamma 1.5 and g_c .00488889 from the Proposition 5 formulas). The decaying tail adds .204545 to the cumulative hazard. The expression on the page adds −.204545. Cumulative hazard is the integral of a positive function, so it can't go down. In the figure the two curves leave delta_T in opposite directions. The area under the decaying one, out to infinity, is the .2045, and the shaded 1,500 years hold 97.4% of it, .1993. The printed integrand leaves the top of the sheet before T + 360. A tail of +.2045 multiplies the survival probability by e to the −.2045, about .815. A tail of −.2045 multiplies it by about 1.23.

Writing the tail with an explicitly positive decay constant would have avoided this, and the page doesn't do that. It verifies that the terminal signed growth rate is approximately g_delta and then divides by g_delta, so the displayed procedure has the sign wrong and it isn't a notational choice. Whether the survival number the draft reports was computed with that sign depends on the code that ran the simulation, which I haven't seen.

Eighty-One Exact Checks and Six Derivatives Pass

Two blocks of checks.py pass. The first takes the Hamiltonian the costate conditions come from and differentiates it six ways, once each for s, ell, sigma, A, B and M, with the derivatives written out by hand. It compares each to a central finite difference at an interior point. The largest gap is 1.2e-9. The sixth derivative, with respect to the survival probability M, is u minus v times the hazard, and that is the line (29) is rearranged from. (This block is in floating point, since finite differences need it, and the assert is at 1e-7.) So the initial costate condition for v is right, and the sign error is in the rearrangement after it.

The second block takes the gamma above 1 growth-rate identities of Proposition 5, the ones behind the headline example, and checks them against the production and hazard equations at 81 parameter combinations: three values each of k, beta, epsilon and gamma, with n = .01, all exact rationals. All 81 pass, and at each one the script also asserts that g_c is positive, g_s is negative and g_s + n is positive. That is a consistency check. It doesn't prove the path exists, is unique, or is reached from every starting point. I also inspected the two appendices in full (A.1 through A.11, B.1 and B.2) for the same kind of slip. The three above are the ones I confirmed.

The 2026 Paper Is a Different Paper

The objection I'd raise myself is that this is a 2019 draft, and the paper that exists now is a different one. That's right, and the audit says so. The version coauthored with Trammell (the copy I retrieved is dated January 5, 2026) uses the correct survival costate relation, v̇ = (rho + delta) v − u, in its equation (47) and in its simulation equation (60). Its model and its transition system are different from the 2019 draft's, so none of the three errors here can be attributed to it without a separate check, and it hasn't had a full audit here. The audit covers the 2019 draft only: the version that was endorsed and circulated in September 2019, at the address the endorsement linked, as downloaded for the audit.

The second objection is that a working draft by a student isn't worth this. It was endorsed in public by a well-known economist, it circulated in a community that prizes exactly this kind of checking, and the checks are 89 lines of standard-library Python. The costate and coupling checks substitute the printed and corrected results into the line they were derived from, as Fractions, and assert on the residuals. The tail block asserts that g_delta is negative and computes the two tail contributions. I read each page as an image, since extracted text can't be trusted on a sign, and audit.md lists what passed, what failed and what it doesn't claim.

Limits

This isn't a formal verification of the draft's existence or optimality claims over an infinite horizon. The transition figures weren't replicated here, and I haven't seen the Matlab the appendix mentions (page 80 names fminsearch). So the three errors are errors in displayed equations, and whether the draft's numbers inherit them is open. The Hamiltonian check is in floating point, and everything else is exact. The audit was done with tooling assistance. The checks use only the standard library and re-run with python3 checks.py.

All three turned up the same way. I took the printed result and returned it to the line it came from. On page 51 that's 100 back into the line above (103), which gives a discount rate of 0 instead of .02.

Appendix: Reproduction

The audit folder holds audit.md, checks.py, results.json and the three page captures, sign.png (page 51), factor.png (page 74) and tail.png (page 80). Page numbers here are the printed ones, and a PDF viewer shows a number one greater. python3 checks.py runs the checks and their assertions and writes results.json. Only the standard library is needed. This is the import and the first check, lines 1 and 6 to 11, verbatim, the toy from page 51:

Python
from fractions import Fraction as F# The costate equation is rho = g_v + u/v - delta.rho, delta, gv, utility = F(2,100), F(1,100), F(0), F(1)v_wrong = utility / (rho-delta+gv)v_right = utility / (rho+delta-gv)assert gv + utility/v_right - delta - rho == 0assert gv + utility/v_wrong - delta - rho != 0

I re-ran the script on 2026-10-13 under Python 3.14.8 and it rewrote results.json byte for byte. This is the file:

JSON
{  "eq103_constant_flow": {    "printed_v": 100.0,    "correct_v": 33.333333333333336,    "printed_costate_residual": -0.02  },  "prop6": {    "gc": 0.005,    "printed_delta": 0.007,    "corrected_candidate_delta": 0.019,    "printed_costate_residual": -0.012  },  "eq174": {    "printed_L": 0.04863013698630137,    "correct_L": 0.05322338830584707,    "printed_residual": -0.004901917808219178  },  "prop5_growth_identity_checks": 81,  "eq194": {    "g_delta": -0.0024444444444444444,    "printed_tail": -0.20454545454545456,    "correct_tail": 0.20454545454545456  },  "hamiltonian_partial_derivatives_max_error": 1.2118248626791228e-09}

Andrew Fribush, software and computer engineer in Austin. I build consequential systems where software meets institutions: finance, healthcare, commerce, government data, and the mechanisms that govern AI agents. Taking new engagements: hello@andrewfribush.com.

I write when a system teaches me something. Get new posts by email: no tracking, one-click unsubscribe.