You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Apollo GraphQL Mutation查询触发Invariant Violation 47错误求助

Apollo GraphQL Mutation触发Invariant Violation:47错误排查

我使用Apollo GraphQL的Mutation接收用户输入更新数据库,点击Update按钮时触发了Invariant Violation:47错误,以下是相关代码及错误信息,需要排查原因和解决方法。

Mutation定义

export const ADD_INSIGHT_META_DATA = gql`
mutation MyMutation ($data : [projectInsightsMetadata_insert_input!]!) {
    insert_projectInsightsMetadata(objects: $data) {
      returning {
        id
        createdAt
      }
    }
  }
`

Mutation调用函数

export const updateScheduleInsightMetaData = async(InsertData:any )=>{
    const response:ApolloQueryResult<any> = await client.query({
        query:ADD_INSIGHT_META_DATA,
        context:{
            role:"updateMasterPlan",
            token:getProjectExchangeToken()
        },
        fetchPolicy: 'network-only',
        variables:{
        data:InsertData
        }
    })
    return response.data;
}

React组件调用代码

import React, { useContext, useEffect, useState } from "react";
import { IconButton, makeStyles, Paper, TextField, Typography } from "@material-ui/core";
import { useParams } from "react-router-dom";
import { Button } from "@material-ui/core";
import { Box } from "@mui/system";

import {getScheduleInsightMetaData,updateScheduleInsightMetaData} from "./InsightsSettingsActions";
import { setIsLoading } from "src/modules/root/context/authentication/action";
import { stateContext } from "src/modules/root/context/authentication/authContext";
import Notification, { AlertTypes } from "src/modules/shared/components/Toaster/Toaster";
import { projectDetailsContext } from "src/modules/baseService/projects/Context/ProjectDetailsContext";
import {
    decodeExchangeToken,
    getProjectExchangeToken
  } from "../../../../../services/authservice";
  import "./InsightsSettings.scss";
   import NoDataMessage from "src/modules/shared/components/NoDataMessage/NoDataMessage";

  interface Params {
    projectId:any
}
interface InputType{
    min:number,
    max:number
}

export const noPermissionMessage = "You don't have permission to view project insight settings";

const InsightsSettings: React.FC =()=>{
    const { projectDetailsState }: any = useContext(projectDetailsContext);
    const { dispatch, stateCont }: any = useContext(stateContext);
    const {projectId} = useParams<Params>();
    const [hasCreateAccess, setCreateAccess] = useState<boolean>(false);
    const [informationalConstraints, setInformationalConstraints] = useState<any>({min:Number(""),max:Number("")});
    const [rfiResponse, setRfiResponse] = useState<any>({min:Number(""),max:Number("")});
    const [physicalConstraints, setPhysicalConstraints] = useState<any>({min:Number(""),max:Number("")});
    const [managementConstraints, setManagementConstraints] = useState<any>({min:Number(""),max:Number("")});
    const tenantId = decodeExchangeToken().tenantId;

    console.log("informationalConstraints",informationalConstraints);
    console.log("rfiResponse",rfiResponse);
    console.log("physicalConstraints",physicalConstraints);
    console.log("managementConstraints",managementConstraints);
    const projectToken = getProjectExchangeToken();
    useEffect(()=>{
    
      setCreateAccess(decodeExchangeToken(projectToken).allowedRoles.includes("viewMasterPlan"))
    },[projectToken])

    const navigateback = () => {
       // props.navBack();
    }

    useEffect(()=>{
       
        const token = projectDetailsState.projectToken;
          fetchInsightsData(projectId,tenantId,token)
      },[projectId,tenantId])
  
      const fetchInsightsData = async(projectId:any,tenantId:any,token:any)=>{
        try{
          // const tenantId = decodeExchangeToken().tenantId;
          // const token = projectDetailsState.projectToken;
          dispatch(setIsLoading(true));
          const res = await getScheduleInsightMetaData(projectId, tenantId ,token)
          dispatch(setIsLoading(false));
  
        }catch(err){
          console.log("error in fetching  insights metadata",err)
          Notification.sendNotification('An error occured while fetching insights metadata', AlertTypes.warn);
          dispatch(setIsLoading(false));
        }
      }


      const handleInformationalInputChange = (e:any)=>{
        setInformationalConstraints((prevState:any)=>({...prevState,[e.target.name]:Number(e.target.value)}))
      }
      const handleRfiInputChange = (e:any)=>{
        setRfiResponse((prevState:any)=>({...prevState,[e.target.name]:Number(e.target.value)}))
      }
      const handlePhysicalInputChange = (e:any)=>{
        setPhysicalConstraints((prevState:any)=>({...prevState,[e.target.name]:Number(e.target.value)}))
      }
      const handleManagementInputChange = (e:any)=>{
        setManagementConstraints((prevState:any)=>({...prevState,[e.target.name]:Number(e.target.value)}))
      }

      const handleUpdate = async(e:any,informationalConstraints:any,rfiResponse:any,physicalConstraints:any,managementConstraints:any)=>{
     try{
        dispatch(setIsLoading(true));
        const data = {
            LeadtimeMgmntConstraints:managementConstraints,
            LeadtimePhysicalConstraints:physicalConstraints,
            ChangeOrderIssueReview:rfiResponse,
            RFIReviewResponse:informationalConstraints
        }
         const response = await updateScheduleInsightMetaData(data)
       // const response = await updateScheduleInsightMetaData(managementConstraints,physicalConstraints,rfiResponse,informationalConstraints)
        Notification.sendNotification("Successfully updated insights", AlertTypes.success);
        dispatch(setIsLoading(false));
     }catch(err){
        dispatch(setIsLoading(false));
        Notification.sendNotification(err, AlertTypes.warn);
        console.log(err)
     }
      }

    return(
         <>
        {hasCreateAccess?
        <div className="InsightsSettings">
        <>
        <div  className="InsightsSettings__header">
        <Typography component="p">Insight Settings</Typography>
        </div>      
        <div className="InsightsSettings__input_area">
            <div className="InsightsSettings__individual_box">
               <Typography component="p">How far ahead of an activity start does the team review and resolve management or informational constraints?</Typography>
               <div className="InsightsSettings__constraints">
                <input name="min"   className="InsightsSettings__constraints__style" value={informationalConstraints.min} placeholder="min" onChange={handleInformationalInputChange}/>
                <input name="max"   className="InsightsSettings__constraints__style" value={informationalConstraints.max} placeholder="max" onChange={handleInformationalInputChange}/>
                </div>
               
               </div>
                <div className="InsightsSettings__individual_box">
                <Typography component="p">What is the average (or typical) RFI design response period for this project?</Typography>
                <div className="InsightsSettings__constraints">
                <input name="min"   className="InsightsSettings__constraints__style" value={rfiResponse.min} placeholder="min" onChange={handleRfiInputChange}/>
                <input name="max"   className="InsightsSettings__constraints__style" value={rfiResponse.max} placeholder="max" onChange={handleRfiInputChange}/>
                </div>
                </div>
                <div className="InsightsSettings__individual_box">
                <Typography component="p">How far ahead of an activity start does the team review and resolve physical or site constraints ?</Typography>
                <div className="InsightsSettings__constraints">
                <input name="min"   className="InsightsSettings__constraints__style" value={physicalConstraints.min} placeholder="min" onChange={handlePhysicalInputChange}/>
                <input name="max"   className="InsightsSettings__constraints__style" value={physicalConstraints.max} placeholder="max" onChange={handlePhysicalInputChange}/>
                </div>
                </div>
               <div className="InsightsSettings__individual_box">
               <Typography component="p">How far ahead of an activity start does the team review and resolve management or informational constraints?</Typography>
               <div className="InsightsSettings__constraints">
               <input name="min"   className="InsightsSettings__constraints__style" value={managementConstraints.min} placeholder="min" onChange={handleManagementInputChange}/>
               <input name="max"   className="InsightsSettings__constraints__style" value={managementConstraints.max} placeholder="max" onChange={handleManagementInputChange}/>
               </div>
               
               </div>
        </div>
        </>
                     <div className="InsightsSettings__action-button">
                        <Button data-testid={'cancel-update'} variant="outlined" onClick={navigateback} className="cancel-button">
                            Cancel
                        </Button>
                        <Button  
                            variant="outlined"
                            className="update-button"
                           onClick={(e:any)=>{handleUpdate(e,informationalConstraints,rfiResponse,physicalConstraints,managementConstraints)}}
                            >
                            Update
                        </Button>
                    </div>
        </div>: (
                    <div className="noCreatePermission-insight">
                        <div className="no-permission-insight">
                            <NoDataMessage message={noPermissionMessage}/> 
                        </div>
                    </div>
                )      }
         </>
    )
}
export default InsightsSettings

错误信息

Invariant Violation: Invariant Violation: 47 (see https://github.com/apollographql/invariant-packages)
    at new InvariantError (http://localhost:3000/static/js/vendors~main.chunk.js:327507:24)
    at invariant (http://localhost:3000/static/js/vendors~main.chunk.js:327521:11)
    at getQueryDefinition (http://localhost:3000/static/js/vendors~main.chunk.js:9716:230)
    at StoreReader.diffQueryAgainstStore (http://localhost:3000/static/js/vendors~main.chunk.js:2449:286)
    at InMemoryCache.diff (http://localhost:3000/static/js/vendors~main.chunk.js:1132:29)
    at http://localhost:3000/static/js/vendors~main.chunk.js:4864:28
    at perform (http://localhost:3000/static/js/vendors~main.chunk.js:1264:31)
    at InMemoryCache.batch (http://localhost:3000/static/js/vendors~main.chunk.js:1288:7)
    at InMemoryCache.performTransaction (http://localhost:3000/static/js/vendors~main.chunk.js:1321:17)
    at QueryInfo.markResult (http://localhost:3000/static/js/vendors~main.chunk.js:4842:20)
    at http://localhost:3000/static/js/vendors~main.chunk.js:5713:19
    at both (http://localhost:3000/static/js/vendors~main.chunk.js:10788:20)
    at http://localhost:3000/static/js/vendors~main.chunk.js:10777:26
    at new Promise (<anonymous>)
    at Object.then (http://localhost:3000/static/js/vendors~main.chunk.js:10776:16)
    at Object.next (http://localhost:3000/static/js/vendors~main.chunk.js:10791:39)
    at notifySubscription (http://localhost:3000/static/js/vendors~main.chunk.js:332559:18)
    at onNotify (http://localhost:3000/static/js/vendors~main.chunk.js:332603:3)
    at SubscriptionObserver.next (http://localhost:3000/static/js/vendors~main.chunk.js:332652:5)
    at http://localhost:3000/static/js/vendors~main.chunk.js:10848:23
    at Array.forEach (<anonymous>)
    at iterateObserversSafely (http://localhost:3000/static/js/vendors~main.chunk.js:10847:23)
    at Object.next (http://localhost:3000/static/js/vendors~main.chunk.js:10586:87)
    at notifySubscription (http://localhost:3000/static/js/vendors~main.chunk.js:332559:18)
    at onNotify (http://localhost:3000/static/js/vendors~main.chunk.js:332603:3)
    at SubscriptionObserver.next (http://localhost:3000/static/js/vendors~main.chunk.js:332652:5)
    at Object.next (http://localhost:3000/static/js/vendors~main.chunk.js:6773:22)
    at notifySubscription (http://localhost:3000/static/js/vendors~main.chunk.js:332559:18)
    at onNotify (http://localhost:3000/static/js/vendors~main.chunk.js:332603:3)
    at SubscriptionObserver.next (http://localhost:3000/static/js/vendors~main.chunk.js:332652:5)
    at notifySubscription (http://localhost:3000/static/js/vendors~main.chunk.js:332559:18)
    at onNotify (http://localhost:3000/static/js/vendors~main.chunk.js:332603:3)
    at SubscriptionObserver.next (http://localhost:3000/static/js/vendors~main.chunk.js:332652:5)
    at http://localhost:3000/static/js/vendors~main.chunk.js:7061:18

原因分析及解决方法

核心原因1:调用Mutation时用错方法

Apollo Client中,client.query()仅用于执行**查询(Query)操作,而变更(Mutation)**必须使用client.mutate()方法。用query()执行Mutation会导致Apollo内部无法识别操作类型,触发Invariant Violation错误。

核心原因2:变量格式不匹配

Mutation定义中$data是数组类型[projectInsightsMetadata_insert_input!]!,但组件传递的是单个对象,不符合参数类型要求。

解决步骤

  1. 修正Mutation调用方法
    将updateScheduleInsightMetaData中的client.query()改为client.mutate(),调整字段名和返回值处理:
export const updateScheduleInsightMetaData = async(InsertData:any )=>{
    const response = await client.mutate({
        mutation: ADD_INSIGHT_META_DATA,
        context:{
            role:"updateMasterPlan",
            token:getProjectExchangeToken()
        },
        variables:{
            data: InsertData
        }
    })
    return response.data;
}
  1. 调整变量为数组格式
    根据数据库表结构,将组件中的data转换为符合projectInsightsMetadata_insert_input类型的数组,示例如下(需匹配实际表字段):
const data = [
    { type: "LeadtimeMgmntConstraints", ...managementConstraints, projectId, tenantId },
    { type: "LeadtimePhysicalConstraints", ...physicalConstraints, projectId, tenantId },
    { type: "ChangeOrderIssueReview", ...rfiResponse, projectId, tenantId },
    { type: "RFIReviewResponse", ...informationalConstraints, projectId, tenantId }
];
  1. 处理空值问题
    组件初始状态{min:Number(""),max:Number("")}会生成NaN,可能导致后端校验失败。建议初始值设为0或空字符串,并在提交前做有效性校验:
// 修改初始状态
const [informationalConstraints, setInformationalConstraints] = useState({min: 0, max: 0});

// 提交前校验
const handleUpdate = async(...) => {
    const isValid = [informationalConstraints, rfiResponse, physicalConstraints, managementConstraints].every(item => {
        return !isNaN(item.min) && !isNaN(item.max);
    });
    if (!isValid) {
        Notification.sendNotification("请输入有效的数字", AlertTypes.warn);
        return;
    }
    // 后续提交逻辑
}

内容的提问来源于stack exchange,提问作者Ashutosh Rai

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 17:25:52