Skip to content

Instantly share code, notes, and snippets.

@cursork
Last active March 14, 2026 22:54
Show Gist options
  • Select an option

  • Save cursork/7104bd5cd71790afba944bbeb115abe2 to your computer and use it in GitHub Desktop.

Select an option

Save cursork/7104bd5cd71790afba944bbeb115abe2 to your computer and use it in GitHub Desktop.
AC0040

AC0040 Reproduction — "could not fix function"

The Bug

Dyalog APL 20.0 throws error AC0040: could not fix function asynchronously when 2 ⎕FIX fails on invalid source inside a Jarvis HTTP request handler, and the code is in a ]link'd namespace.

The error is:

  • Category: "Object oriented programming", ENX 40
  • Fires asynchronously (different thread from the caller, ~0.5s delay)
  • Not catchable by :Trap 0 in the calling function
  • Not produced by any APL code in Link (confirmed by instrumentation)
  • Generated by the interpreter's internal C code
  • 100% reproducible

Requirements

  • Dyalog APL 20.0
  • Stark (REST framework wrapping Jarvis)
  • Link (bundled with Dyalog)
  • Conga (bundled with Dyalog)

How to Reproduce

]link.create stark /path/to/Stark/APLSource
]link.create repro /path/to/AC0040/APLSource
repro.Tests.RunTest 0

Expected output:

POST /upload →200
error AC0040: could not fix function
∧
Complete: 1 error.
Done

What the Repro Does

Two files:

Wrapper.aplc — A :Class that wraps Stark with one endpoint (POST /upload). The handler:

  1. Parses the JSON body
  2. Calls 2 tmpns.⎕FIX lines where lines is the submitted source (invalid APL)
  3. Catches the expected ⎕FIX failure in :Trap 0
  4. Returns a response

Tests/RunTest.aplf — Starts the server and sends a POST with source: 'this is not valid APL'.

Investigation Summary

Through binary search of a larger test function (178 lines), we isolated the trigger to a single operation: 2 ⎕FIX failing on invalid source inside a Jarvis HTTP thread.

The following do NOT trigger AC0040:

  • 2 ⎕FIX failing in the main thread (same namespace, same invalid source)
  • 2 ⎕FIX failing in a plain &-spawned thread
  • 2 ⎕FIX failing in a class method on a &-spawned thread
  • 2 ⎕FIX failing via 85⌶ + :Hold + :Trap 85 on a &-spawned thread
  • 2 ⎕FIX failing in nested class threads (class method spawning handler thread)
  • 2 ⎕FIX succeeding in a Jarvis HTTP thread (valid source)
  • Any Jarvis HTTP request that doesn't call ⎕FIX

Additionally:

  • File ties are not the cause — fires with -watch=ns on both links
  • Link APL code is not involvedOnAfterFix, Fix, and Notify were instrumented and never fire
  • The error is interpreter-internal — the "Complete: N error." format does not exist in any APL source

See WALKTHROUGH.md for the full bug report with detailed findings.

Environment

  • macOS (Darwin 25.3.0)
  • Dyalog APL 20.0
  • Stark 1.x / Jarvis 1.22.2
  • Conga 3.6
  • Link (current version bundled with Dyalog 20.0)
RunTest dummy;app;r;port;base;payload
⍝ AC0040 reproduction — minimal
⍝ Run with:
⍝ ]link.create stark /path/to/Stark/APLSource
⍝ ]link.create repro /path/to/AC0040/APLSource
⍝ repro.Tests.RunTest 0
⎕IO←0
⎕SE.SALT.Load'HttpCommand'
port←19900+?100
base←'http://localhost:',⍕port
app←⎕NEW ##.Wrapper (#.stark.Stark)
app.Start port
⍝ POST with invalid APL source → ⎕FIX fails in Jarvis thread → AC0040
payload←⎕JSON(source: 'this is not valid APL')
r←HttpCommand.Do'POST'(base,'/upload')payload('content-type' 'application/json')
⎕←'POST /upload →',⍕r.HttpStatus
⎕DL 2
⎕←'Done'
app.Stop

Bug Report: AC0040 "could not fix function"

Summary

2 ⎕FIX failing on invalid source inside a Jarvis HTTP request handler produces an asynchronous error AC0040: could not fix function — but only when the code lives in a ]LINK'd namespace.

The error cannot be caught with :Trap. It fires on a different thread roughly half a second after the ⎕FIX failure. It is not generated by any APL code in Link, Stark, or Jarvis — it appears to originate from the interpreter itself.

Environment

  • Dyalog APL 20.0, macOS (Darwin 25.3.0)
  • Jarvis 1.22.2 (via Stark)
  • Conga 3.6
  • Link (as bundled with Dyalog 20.0)

Reproduction

Prerequisites

Files

Two files in APLSource/:

Wrapper.aplc — A class with a single HTTP endpoint. The endpoint receives a JSON body, extracts a source field, and attempts 2 ⎕FIX on it. The ⎕FIX is wrapped in :Trap 0:

:Class Wrapper
    :Field Private router

     Make starkClass
      :Access Public
      :Implements Constructor
      router⎕NEW starkClass
      router.Handlers⎕THIS
    

     resultUpload req;body;tmpns;lines
      :Access Public
      :Trap 0
          body⎕JSON req.Body
          lines(⎕UCS 10)()body.source
          tmpns⎕NS''
          2 tmpns.⎕FIX lines
      :Else
          result(error: ⎕DMX.Message)
          :Return
      :EndTrap
      result(status: 'ok')
    

     Start port
      :Access Public
      '/upload' router.Post 'Upload'
      router.Start port
    

     Stop
      :Access Public
      router.Stop
    
:EndClass

Tests/RunTest.aplf — Starts the server and sends one POST request containing invalid APL source:

 RunTest dummy;app;r;port;base;payload
 ⎕IO0
 ⎕SE.SALT.Load'HttpCommand'
 port19900+?100
 base'http://localhost:',port
 app⎕NEW ##.Wrapper (#.stark.Stark)
 app.Start port
 payload⎕JSON(source: 'this is not valid APL')
 rHttpCommand.Do'POST'(base,'/upload')payload('content-type' 'application/json')
 'POST /upload →',r.HttpStatus
 ⎕DL 2
 'Done'
 app.Stop

Steps

      ]link.create stark /path/to/Stark/APLSource
Linked: #.stark ←→ /path/to/Stark/APLSource

      ]link.create repro /path/to/AC0040/APLSource
Linked: #.repro ←→ /path/to/AC0040/APLSource

      repro.Tests.RunTest 0

Expected

The ⎕FIX fails, :Trap 0 catches it, the endpoint returns a JSON error response, the test prints POST /upload →200 and finishes cleanly.

Actual

The endpoint handles the error correctly and returns 200. But roughly half a second later, this appears asynchronously in the session:

error AC0040: could not fix function
∧
Complete: 1 error.

This error is not catchable. It fires on a different thread. In a larger test suite, it can derail unrelated :Trap 0 blocks running at the time.

Isolation testing

Through systematic binary search of a larger function (178 lines) that originally triggered this, we isolated the trigger to a single operation: 2 ⎕FIX failing on invalid source inside a Jarvis HTTP request handler.

What triggers AC0040 and what doesn't

Scenario AC0040?
2 ⎕FIX fails in a Jarvis thread, namespace is ]LINK'd Yes — always
2 ⎕FIX fails in the main thread (same linked namespace, same invalid source) No
2 ⎕FIX fails in a plain &-spawned thread (same linked namespace) No
2 ⎕FIX fails in a class method on a &-spawned thread No
2 ⎕FIX fails via 85⌶ (as Jarvis uses) on a &-spawned thread No
2 ⎕FIX fails via 85⌶ + :Hold + :Trap 85 on a &-spawned thread No
2 ⎕FIX fails in nested class threads (class method spawning handler thread) No
2 ⎕FIX succeeds in a Jarvis thread (valid source, linked namespace) No
Any Jarvis request that doesn't call ⎕FIX (linked namespace) No
]link.create with -watch=ns (no file ties) — 2 ⎕FIX fails in Jarvis thread Yes
Both stark and repro linked with -watch=ns Yes

In short: the error requires the intersection of linked namespace + Jarvis HTTP request processing + failed 2 ⎕FIX. We attempted to reproduce without Jarvis by replicating its ingredients (&-threads, class methods, 85⌶, :Hold, nested class threads) but could not trigger AC0040 outside the full Jarvis stack.

File ties are not the cause

Link's FixTie function ties APL objects to source files when watch≡'both' (the default). We tested with -watch=ns on both linked namespaces, which disables file tying entirely. AC0040 still fires. The link arrows confirm the change took effect ( instead of ←→).

Link is not involved

We instrumented Link's three entry points by patching them at runtime to log calls:

  • ⎕SE.Link.OnAfterFix — editor/afterfix callback
  • ⎕SE.Link.Fix — the fix orchestrator
  • ⎕SE.Link.Notify — file system watcher callback

All three were patched with ⎕← logging before the server started. The patches were confirmed successful (each ⎕FX returned the function name). None of them fired during the test — no >>> output appeared.

This proves AC0040 is not produced by any APL code in Link. Link's callbacks are completely uninvolved.

The error is interpreter-internal

The error format error AC0040: could not fix function + + Complete: 1 error. does not appear in any APL source we can search:

  • Not in Link source (~/dev/link/)
  • Not in Stark/Jarvis source
  • Not in Dyalog's bundled APL files (SALT/, workspace files)
  • The "Complete:" string does not appear in any .aplf, .aplc, .apln, or .dyalog file

The error code AC0040 maps to ⎕DMX.(Category ENX)≡'Object oriented programming' 40 in Dyalog's diagnostic table (SALT/spice/table.tsv).

This all points to the error being generated by the interpreter's internal C code — not by any APL-level callback, hook, or error handler.

What we think is happening

The Jarvis request-handling path involves Conga's event loop (LDRC.Wait), thread spawning from within class methods, and 85⌶ execution of handler functions. Something in this specific combination — which we could not replicate with any subset of these mechanisms — causes the interpreter to asynchronously attempt an internal ⎕FIX operation after the user-level 2 ⎕FIX fails. That internal operation fails with AC0040, which surfaces as an uncatchable session error.

Impact

The asynchronous error disrupts any code running under :Trap 0 at the time it fires. In the application where this was discovered (a game server with an API test suite), the AC0040 from a bot-upload validation endpoint would derail the test runner's error handling, producing misleading DOMAIN ERRORs instead of the actual test results.

A workaround exists (avoid calling 2 ⎕FIX for validation inside request handlers), but 2 ⎕FIX inside :Trap 0 should fail silently regardless of context.

:Class Wrapper
⍝ Minimal AC0040 reproduction
⍝ The /upload endpoint calls 2 ⎕FIX on invalid source
⍝ Triggers AC0040 asynchronously when run in a linked namespace
:Field Private router
∇ Make starkClass
:Access Public
:Implements Constructor
router←⎕NEW starkClass
router.Handlers←⎕THIS
∇ result←Upload req;body;tmpns;lines
:Access Public
:Trap 0
body←⎕JSON req.Body
lines←(⎕UCS 10)(≠⊆⊢)body.source
tmpns←⎕NS''
2 tmpns.⎕FIX lines
:Else
result←(error: ⎕DMX.Message)
:Return
:EndTrap
result←(status: 'ok')
∇ Start port
:Access Public
'/upload' router.Post 'Upload'
router.Start port
∇ Stop
:Access Public
router.Stop
:EndClass
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment