Skip to content

[fix][evaluation] guard nil BaseInfo and nil correction to prevent panics in experiment result and record correction paths - #628

Open
xueyizheng wants to merge 1 commit into
mainfrom
fix/eval-nil-pointer-panic-0824
Open

[fix][evaluation] guard nil BaseInfo and nil correction to prevent panics in experiment result and record correction paths#628
xueyizheng wants to merge 1 commit into
mainfrom
fix/eval-nil-pointer-panic-0824

Conversation

@xueyizheng

@xueyizheng xueyizheng commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

What type of PR is this?
fix
Check the PR title
[x] This PR title match the format: [][]
[x] The description of this PR title is user-oriented and clear enough for others to understand.
[ ] Add documentation if the current PR requires user awareness at the usage level.
[x] This PR is written in English. PRs not in English will not be reviewed.
(Optional) More detailed description for this PR
Fixes two independent nil pointer dereferences in the evaluation module. Both follow the same shape: a pointer field is dereferenced before it is checked, so a single record or request with a missing optional field takes down the whole call instead of degrading gracefully.

  1. EvalTargetRecordDO2DTO dereferences src.BaseInfo unconditionally
    The DTO is built with CreatedAt: src.BaseInfo.CreatedAt inside the struct literal. The if src.BaseInfo != nil check below it is dead as a guard, because the dereference already happened while evaluating the literal.
    BaseInfo is an optional field on EvalTargetRecord, so any record that has it unset panics the conversion. Since this convertor runs in the fan-out of BatchGetExperimentResult, one such record fails the entire batch rather than just its own row, and the caller sees a 5xx instead of partial results.
    Fix: initialize an empty BaseInfo in the literal and populate the timestamps inside the existing nil check. The CreatedBy / UpdatedBy TODOs move with them, so they are guarded too when someone eventually fills them in.
  2. CorrectEvaluatorRecord dereferences correctionDO on its first line
    EvaluatorRecordServiceImpl.CorrectEvaluatorRecord starts with correctionDO.UpdatedBy = userIDInContext, before any validation. The method already defensively initializes EvaluatorOutputData, EvaluatorResult and BaseInfo a few lines further down, so the intent is clearly to tolerate a sparsely populated record — the correction argument was simply missed.
    Correction is optional in the request, and OpenAPICorrectionDTO2DO returns nil for a nil DTO by design. So a CorrectEvaluatorRecordOApi call that omits correction reaches the domain service with correctionDO == nil and panics on a caller mistake that should have been a 4xx.
    Fix, at two levels:
    domain service: reject nil evaluatorRecordDO / correctionDO with
    CommonInvalidParamCode before touching either.
    OpenAPI handler: validate req.Correction up front, so a malformed request
    gets a structured invalid-param error and never reaches the domain layer.
    Tests
    Three regression tests, one per guarded entry point:
    TestEvalTargetRecordDO2DTO_NilBaseInfo — a record with BaseInfo: nil
    converts without panicking and yields a non-nil, empty BaseInfo.
    TestEvaluatorRecordServiceImpl_CorrectEvaluatorRecord_NilParams — all three
    nil combinations return an error, and the repo / publisher mocks assert
    Times(0) so validation is proven to run before any side effect.
    eval_openapi_app_test.go gains a nil correction case asserting
    CommonInvalidParamCode with auth and the record service never invoked.
    Each test was checked against the unpatched code and fails there with invalid memory address or nil pointer dereference, so they pin the actual defect rather than passing vacuously.
    Two pre-existing cases in TestEvalOpenAPIApplication_CorrectEvaluatorRecordOApi (record not found, auth failed) omitted Correction and are now short-circuited by the new up-front check. They were given a valid correction so they still exercise their intended paths.
    Note for reviewers: these packages need -gcflags="all=-N -l" (required by mockey); without it unrelated cases fail their own self-check.

线上两个独立 panic,同属判空缺失:

1. SG BatchGetExperimentResult → EvalTargetRecordDO2DTO:
   构造 DTO 时在结构体字面量里直接取 src.BaseInfo.CreatedAt,
   后面虽有 if src.BaseInfo != nil 保护,但发生在直接访问之后。
   BaseInfo 为 nil 的 target record 会打挂整次请求,网关侧表现为 502。
   改为先构造空 BaseInfo,判空后再赋值。

2. CN CorrectEvaluatorRecordOApi → CorrectEvaluatorRecord:
   领域实现第一行即 correctionDO.UpdatedBy = ...,
   请求未带 correction 时 correctionDO 为 nil,直接 panic。
   在领域层加 record / correction 判空返回参数错误,
   并在 OApi 层前置校验 correction,缺失时返回结构化 4xx 而非透传。

补回归单测:两处均验证 nil 入参不 panic;nil 参数校验先于任何
repo / publisher 调用生效。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@xueyizheng xueyizheng changed the title [fix][evaluation] 修复评测结果转换与评估记录修正的两处 nil pointer panic [fix][evaluation] guard nil BaseInfo and nil correction to prevent panics in experiment result and record correction paths Aug 24, 2026
@bitholic
bitholic self-requested a review August 24, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants