{"record":{"id":"e815da6d705540e0","repo":"clockworklabs/SpacetimeDB","slug":"error-skipping-procedure-registration-s-becaus","errorCode":null,"errorMessage":"ERROR: Skipping procedure registration '%s' because circular reference error is set\n","messagePattern":"ERROR: Skipping procedure registration '(.+?)' because circular reference error is set\n","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/bindings-cpp/include/spacetimedb/internal/v10_builder.h","lineNumber":535,"sourceCode":"        uint32_t index = static_cast<uint32_t>(is_anonymous ? (GetAnonymousViewHandlerCount() - 1) : (GetViewHandlerCount() - 1));\n\n        RawViewDefV10 view_def{\n            view_name,\n            index,\n            is_public,\n            is_anonymous,\n            ProductType{},\n            return_type,\n        };\n        UpsertView(view_def);\n    }\n\n    template<typename Func>\n    void RegisterProcedure(const std::string& procedure_name,\n                           Func func,\n                           const std::vector<std::string>& param_names = {}) {\n        if (g_circular_ref_error) {\n            std::fprintf(stderr, \"ERROR: Skipping procedure registration '%s' because circular reference error is set\\n\",\n                        procedure_name.c_str());\n            return;\n        }\n        using traits = function_traits<Func>;\n        using ReturnType = typename traits::result_type;\n        static_assert(traits::arity > 0, \"Procedure must have at least one parameter (ProcedureContext)\");\n        if constexpr (traits::arity > 0) {\n            using FirstParamType = std::remove_cv_t<std::remove_reference_t<typename traits::template arg_t<0>>>;\n            static_assert(std::is_same_v<FirstParamType, ProcedureContext>,\n                \"First parameter of procedure must be ProcedureContext\");\n        }\n\n        std::function<std::vector<uint8_t>(ProcedureContext&, BytesSource)> handler;\n        if constexpr (traits::arity == 1) {\n            handler = [func](ProcedureContext& ctx, BytesSource) -> std::vector<uint8_t> {\n                auto result = func(ctx);\n                IterBuf buf = IterBuf::take();\n                {","sourceCodeStart":517,"sourceCodeEnd":553,"githubUrl":"https://github.com/clockworklabs/SpacetimeDB/blob/524b4487d949b61a07d4f39c862d1290259dfd20/crates/bindings-cpp/include/spacetimedb/internal/v10_builder.h#L517-L553","documentation":"RegisterProcedure skips an HTTP-callable procedure registration because g_circular_ref_error is already set by a prior type-cycle detection during static module build. The procedure's static_assert on ProcedureContext is not what failed — the module-wide poison flag is. The module will subsequently fail preinit_99 validation, so the procedure never becomes callable.","triggerScenarios":"An ST_PROCEDURE macro (procedure_macros.h) instantiates a registration that calls getV10Builder().RegisterProcedure('<name>', fn) in a module whose typespace also contains a recursive type — often a procedure parameter or shared DTO that contains itself via Vec/Option. The flag is set during type building, before the procedure registration runs.","commonSituations":"Adding a JSON/HTTP procedure whose payload struct is recursive (e.g. a tree DTO); a refactor that makes two DTOs reference each other in a module that already exposes procedures; moving types between headers so registration order changes.","solutions":["Resolve the circular reference reported by '[CIRCULAR REFERENCE DETECTED]' earlier in the build/runtime log","Make recursive DTOs acyclic: children become std::vector<uint64_t> or flat lists instead of nested values","Rebuild and verify the procedure is listed in the published module","If multiple 'Skipping ...' lines appear, they share this single root cause — fix it once"],"exampleFix":"// before — recursive DTO blocks procedure registration\nstruct TreeDto { std::string label; std::vector<TreeDto> kids; };\n// after\nstruct TreeDto { std::string label; std::vector<uint32_t> kid_ids; };","handlingStrategy":"validation","validationCode":"#include <spacetimedb/internal/module_type_registration.h>\nTEST(Module, ProceduresRegistered) {\n    EXPECT_FALSE(SpacetimeDB::Internal::g_circular_ref_error);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Flatten recursive DTOs used by procedures into ID-referencing shapes","Smoke-test each procedure endpoint after publish","Assert the global flag is clear in CI to catch type cycles before they hide registrations"],"tags":["spacetimedb","cpp","procedure","circular-reference","schema","static-initialization"],"backgroundTag":"circular-type-reference","analyzedSha":"524b4487d949b61a07d4f39c862d1290259dfd20","analyzedAt":"2026-08-16T23:58:54.611Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}