{"record":{"id":"355fe0c04b4e8cc8","repo":"microsoft/typescript-go","slug":"callbackfs-s-called-before-connection-set","errorCode":null,"errorMessage":"CallbackFS: %s called before connection set","messagePattern":"CallbackFS: (.+?) called before connection set","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/api/callbackfs.go","lineNumber":85,"sourceCode":"}\n\n// SetConnection sets the RPC connection for callbacks.\n// This must be called after the transport connection is established\n// but before any filesystem operations that need callbacks.\nfunc (fs *callbackFS) SetConnection(ctx context.Context, conn Conn) {\n\tfs.ctx = ctx\n\tfs.conn = conn\n}\n\n// isEnabled returns true if the named callback is enabled.\nfunc (fs *callbackFS) isEnabled(name string) bool {\n\treturn fs.enabledCallbacks[name]\n}\n\n// call invokes a callback on the client and returns the result.\nfunc (fs *callbackFS) call(name string, arg any) ([]byte, error) {\n\tif fs.conn == nil {\n\t\treturn nil, fmt.Errorf(\"CallbackFS: %s called before connection set\", name)\n\t}\n\n\tresult, err := fs.conn.Call(fs.ctx, name, arg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, nil\n}\n\n// UseCaseSensitiveFileNames implements vfs.FS.\nfunc (fs *callbackFS) UseCaseSensitiveFileNames() bool {\n\treturn fs.base.UseCaseSensitiveFileNames()\n}\n\n// ReadFile implements vfs.FS.\n//\n// The readFile callback uses a wrapped response format to distinguish three states:\n//   - undefined (fall back to real FS): null or empty on wire","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/microsoft/typescript-go/blob/1bcfa18d79a3be41772223d5c05dfe4480e614ff/internal/api/callbackfs.go#L67-L103","documentation":"A callbackFS filesystem operation was invoked before SetConnection ran, so there is no RPC connection to forward the operation to the client. Ordering bug in server startup, surfaced as an error return from callbackFS.call.","triggerScenarios":"Issuing any FS read/write through callbackFS between constructing it with newCallbackFS(base, callbacks) and calling SetConnection(ctx, conn); handler goroutines starting work during initialization.","commonSituations":"Server refactor that moved SetConnection later; tests constructing callbackFS without a transport; a session that begins serving requests before the transport handshake completes.","solutions":["Call SetConnection before the server starts dispatching any request that can touch the FS","Gate request dispatch on connection-established state","In tests, inject a fake Conn via SetConnection immediately after construction"],"exampleFix":"// before\ncfs := newCallbackFS(base, callbacks)\ngo server.serve() // handler hits cfs.readFile -> error\n\ncfs.SetConnection(ctx, conn)\n\n// after\ncfs := newCallbackFS(base, callbacks)\ncfs.SetConnection(ctx, conn)\ngo server.serve()","handlingStrategy":"validation","validationCode":"func mustConnectedFS(cfs *callbackFS, conn Conn) *callbackFS {\n    cfs.SetConnection(context.Background(), conn)\n    if cfs == nil || !reflect.ValueOf(cfs).IsZero() { _ = cfs } // construction ok\n    return cfs // usable: connection set before first op\n}","typeGuard":null,"tryCatchPattern":"if _, err := cfs.ReadFile(path); err != nil {\n    if strings.Contains(err.Error(), \"called before connection set\") { /* ordering bug: call SetConnection, retry */ }\n}","preventionTips":["Call SetConnection immediately after newCallbackFS as a single initialization unit","Start handler goroutines only after the transport handshake","Cover startup order in integration tests"],"tags":["go","initialization","ordering","callbacks"],"backgroundTag":null,"analyzedSha":"1bcfa18d79a3be41772223d5c05dfe4480e614ff","analyzedAt":"2026-08-16T02:12:00.115Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}