{"record":{"id":"85110489a175feea","repo":"vxcontrol/pentagi","slug":"failed-to-get-tool-call-log-w","errorCode":null,"errorMessage":"failed to get tool call log: %w","messagePattern":"failed to get tool call log: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/pkg/controller/tclog.go","lineNumber":122,"sourceCode":"\t\tDurationSeconds: durationSeconds,\n\t\tID:              id,\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to update tool call log failed result: %w\", err)\n\t}\n\n\tw.pub.ToolCallLogUpdated(ctx, tc)\n\n\treturn nil\n}\n\nfunc (w *flowToolCallLogWorker) GetLog(ctx context.Context, id int64) (database.Toolcall, error) {\n\ttc, err := w.db.GetFlowToolcall(ctx, database.GetFlowToolcallParams{\n\t\tID:     id,\n\t\tFlowID: w.flowID,\n\t})\n\tif err != nil {\n\t\treturn database.Toolcall{}, fmt.Errorf(\"failed to get tool call log: %w\", err)\n\t}\n\n\treturn tc, nil\n}\n","sourceCodeStart":104,"sourceCodeEnd":127,"githubUrl":"https://github.com/vxcontrol/pentagi/blob/ea665308baaff015b226f308438a68d929d0f29b/backend/pkg/controller/tclog.go#L104-L127","documentation":"GetLog fetches one toolcall row scoped by both id and flowID via db.GetFlowToolcall. Because the query filters on the worker's flowID, requesting an ID that exists but belongs to another flow returns sql.ErrNoRows, wrapped here. Any other DB failure (connection, canceled ctx) is wrapped identically.","triggerScenarios":"Requesting a toolcall ID from a different flow's worker; querying an ID after the flow/rows were deleted; passing an unparsed/zero id; DB unavailable.","commonSituations":"GraphQL/REST resolvers forwarding client-supplied IDs without scoping; frontend caches holding IDs from a re-created flow; cross-worker lookups in tool implementations.","solutions":["Check errors.Is(err, sql.ErrNoRows) and surface 404 (not 500) to the caller","Ensure you use the FlowToolCallLogWorker obtained via GetFlowToolCallLog for that specific flow","Validate id > 0 before calling","If the caller has only a toolcall ID, look up its flow first, then fetch the matching worker","Retry transient DB errors with backoff"],"exampleFix":"// before\ntc, err := wrongFlowWorker.GetLog(ctx, toolcallID) // scope mismatch\n// after\nworker, err := ctl.GetFlowToolCallLog(ctx, flowID)\nif err != nil { return nil, err }\ntc, err := worker.GetLog(ctx, toolcallID)\nif errors.Is(err, sql.ErrNoRows) { return nil, ErrNotFound }","handlingStrategy":"type-guard","validationCode":"func validToolcallID(id int64) bool { return id > 0 }","typeGuard":"func toolcallNotFound(err error) bool { return errors.Is(err, sql.ErrNoRows) }","tryCatchPattern":"tc, err := worker.GetLog(ctx, id)\nif toolcallNotFound(err) {\n    return nil, newNotFound(\"tool call\", id) // 404, not 500\n}\nif err != nil {\n    return nil, fmt.Errorf(\"get toolcall: %w\", err)\n}","preventionTips":["Always fetch the worker via GetFlowToolCallLog(flowID) matching the toolcall's flow","Validate client-supplied IDs are positive int64 before querying","Map sql.ErrNoRows to 404 at the handler boundary","Expect cross-flow IDs to return not-found (the query filters on flowID) — never treat as a bug","Add an integration test asserting cross-flow lookups return no rows"],"tags":["database","not-found","scoping"],"backgroundTag":"sql-no-rows","analyzedSha":"ea665308baaff015b226f308438a68d929d0f29b","analyzedAt":"2026-09-01T14:16:31.421Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}